diff --git a/.solhintignore b/.solhintignore index 821516fc..7c569c83 100644 --- a/.solhintignore +++ b/.solhintignore @@ -7,11 +7,6 @@ **/lib/VerifierStateTransition.sol **/lib/VerifierV3.sol **/lib/VerifierV3Wrapper.sol -**/lib/groth16-verifiers/Groth16VerifierMTP.sol -**/lib/groth16-verifiers/Groth16VerifierMTPWrapper.sol -**/lib/groth16-verifiers/Groth16VerifierSig.sol -**/lib/groth16-verifiers/Groth16VerifierSigWrapper.sol -**/lib/groth16-verifiers/Groth16VerifierStateTransition.sol -**/lib/groth16-verifiers/Groth16VerifierV3.sol -**/lib/groth16-verifiers/Groth16VerifierV3Wrapper.sol +**/lib/groth16-verifiers **/node_modules + diff --git a/contracts/AlwaysRevert.sol b/contracts/AlwaysRevert.sol index 7f9bf943..93c57422 100644 --- a/contracts/AlwaysRevert.sol +++ b/contracts/AlwaysRevert.sol @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; +error TheContractIsDisabled(); + /// @title This contract as a dummy implementation for Proxy contract if we need to revert all calls // This can be applied to disable all methods of a proxy contract with explicit error message contract AlwaysRevert { fallback() external payable { - revert("The contract is disabled"); + revert TheContractIsDisabled(); } } diff --git a/contracts/Create2AddressAnchor.sol b/contracts/Create2AddressAnchor.sol index 4913ffc1..e43d8108 100644 --- a/contracts/Create2AddressAnchor.sol +++ b/contracts/Create2AddressAnchor.sol @@ -1,4 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; +/* solhint-disable no-empty-blocks */ contract Create2AddressAnchor {} +/* solhint-enable no-empty-blocks */ diff --git a/contracts/cross-chain/CrossChainProofValidator.sol b/contracts/cross-chain/CrossChainProofValidator.sol index 09f271f2..b88a3518 100644 --- a/contracts/cross-chain/CrossChainProofValidator.sol +++ b/contracts/cross-chain/CrossChainProofValidator.sol @@ -6,6 +6,13 @@ import {ICrossChainProofValidator} from "../interfaces/ICrossChainProofValidator import {IState} from "../interfaces/IState.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +error IdentityStateProofInvalid(); +error GlobalStateProofInvalid(); +error GlobalStateProofSigningAddressInvalid(address recovered); +error IdentityStateProofSigningAddressInvalid(address recovered); +error OracleTimestampCannotBeInThePast(); +error OracleReplacedAtTimestampCannotBeInTheFuture(); + /** * @dev Contract which provides proof validation from identity state * and global state proofs from trusted oracle with signature from any network. @@ -83,11 +90,12 @@ contract CrossChainProofValidator is Ownable, EIP712, ICrossChainProofValidator gsu.globalStateMsg, gsu.signature ); - require(isValid, "Global state proof is not valid"); - require( - recovered == _oracleSigningAddress, - "Global state proof signing address is not valid" - ); + if (!isValid) { + revert GlobalStateProofInvalid(); + } + if (recovered != _oracleSigningAddress) { + revert GlobalStateProofSigningAddressInvalid(recovered); + } return IState.GlobalStateProcessResult({ @@ -117,11 +125,12 @@ contract CrossChainProofValidator is Ownable, EIP712, ICrossChainProofValidator isu.idStateMsg, isu.signature ); - require(isValid, "Identity state proof is not valid"); - require( - recovered == _oracleSigningAddress, - "Identity state proof signing address is not valid" - ); + if (!isValid) { + revert IdentityStateProofInvalid(); + } + if (recovered != _oracleSigningAddress) { + revert IdentityStateProofSigningAddressInvalid(recovered); + } return IState.IdentityStateProcessResult({ @@ -213,13 +222,13 @@ contract CrossChainProofValidator is Ownable, EIP712, ICrossChainProofValidator uint256 replacedAtTimestamp ) internal view returns (uint256 replacedAt) { if (oracleTimestamp < block.timestamp - MAX_TIMESTAMP_LAG) { - revert("Oracle timestamp cannot be in the past"); + revert OracleTimestampCannotBeInThePast(); } replacedAt = replacedAtTimestamp == 0 ? oracleTimestamp : replacedAtTimestamp; if (replacedAt > block.timestamp + MAX_REPLACED_AT_AHEAD_OF_TIME) { - revert("Oracle replacedAtTimestamp or oracle timestamp cannot be in the future"); + revert OracleReplacedAtTimestampCannotBeInTheFuture(); } // this should never happen as block.timestamp is always greater than 0 diff --git a/contracts/identitytreestore/IdentityTreeStore.sol b/contracts/identitytreestore/IdentityTreeStore.sol index fa636c7d..a2a4eaef 100644 --- a/contracts/identitytreestore/IdentityTreeStore.sol +++ b/contracts/identitytreestore/IdentityTreeStore.sol @@ -8,6 +8,11 @@ import {IOnchainCredentialStatusResolver} from "../interfaces/IOnchainCredential import {IRHSStorage} from "../interfaces/IRHSStorage.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +error NodeNotFound(); +error InvalidStateNode(); +error InvalidNodeType(); +error UnsupportedLength(); + /** * @dev Contract which provides onchain Reverse Hash Service (RHS) * for checking revocation status of claims. @@ -40,10 +45,12 @@ contract IdentityTreeStore is Initializable, IOnchainCredentialStatusResolver, I // keccak256(abi.encode(uint256(keccak256("iden3.storage.IdentityTreeStore.ReverseHashLibData")) - 1)) & // ~bytes32(uint256(0xff)); + // solhint-disable-next-line const-name-snakecase bytes32 private constant ReverseHashLibDataStorageLocation = 0x0f7e3bdc6cc0e880d509aa1f6b8d1a88e5fcb7274e18dfba772424a36fe9b400; function _getReverseHashLibDataStorage() private pure returns (ReverseHashLib.Data storage $) { + // solhint-disable-next-line no-inline-assembly assembly { $.slot := ReverseHashLibDataStorageLocation } @@ -55,6 +62,7 @@ contract IdentityTreeStore is Initializable, IOnchainCredentialStatusResolver, I } // keccak256(abi.encode(uint256(keccak256("iden3.storage.IdentityTreeStore.Main")) - 1)) & ~bytes32(uint256(0xff)); + // solhint-disable-next-line const-name-snakecase bytes32 private constant IdentityTreeStoreMainStorageLocation = 0x95ca427007e091a13a7ccfcb233b8a2ed19d987330a248c445b1b483a35bb800; @@ -64,6 +72,7 @@ contract IdentityTreeStore is Initializable, IOnchainCredentialStatusResolver, I pure returns (IdentityTreeStoreMainStorage storage $) { + // solhint-disable-next-line no-inline-assembly assembly { $.slot := IdentityTreeStoreMainStorageLocation } @@ -96,7 +105,9 @@ contract IdentityTreeStore is Initializable, IOnchainCredentialStatusResolver, I */ function getNode(uint256 key) public view returns (uint256[] memory) { uint256[] memory preim = _getReverseHashLibDataStorage().getPreimage(key); - require(preim.length > 0, "Node not found"); + if (preim.length == 0) { + revert NodeNotFound(); + } return preim; } @@ -122,6 +133,7 @@ contract IdentityTreeStore is Initializable, IOnchainCredentialStatusResolver, I * @return CredentialStatus */ function getRevocationStatusByIdAndState( + // solhint-disable-next-line no-unused-vars uint256 id, uint256 state, uint64 nonce @@ -134,7 +146,9 @@ contract IdentityTreeStore is Initializable, IOnchainCredentialStatusResolver, I uint64 nonce ) internal view returns (CredentialStatus memory) { uint256[] memory roots = getNode(state); - require(_nodeType(roots) == NodeType.State, "Invalid state node"); + if (_nodeType(roots) != NodeType.State) { + revert InvalidStateNode(); + } CredentialStatus memory status = CredentialStatus({ issuer: IdentityStateRoots({ @@ -200,7 +214,7 @@ contract IdentityTreeStore is Initializable, IOnchainCredentialStatusResolver, I proof.siblings[i] = children[1]; } } else { - revert("Invalid node type"); + revert InvalidNodeType(); } } @@ -234,6 +248,6 @@ contract IdentityTreeStore is Initializable, IOnchainCredentialStatusResolver, I if (preimage.length == 3) { return PoseidonUnit3L.poseidon([preimage[0], preimage[1], preimage[2]]); } - revert("Unsupported length"); + revert UnsupportedLength(); } } diff --git a/contracts/imports.sol b/contracts/imports.sol index 4cdc83cf..61554a96 100644 --- a/contracts/imports.sol +++ b/contracts/imports.sol @@ -1,7 +1,9 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; // We import these here to force Hardhat to compile them. // This ensures that their artifacts are available for Hardhat Ignition to use. +/* solhint-disable no-unused-import */ import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +/* solhint-enable no-unused-import */ diff --git a/contracts/interfaces/IAuthValidator.sol b/contracts/interfaces/IAuthValidator.sol new file mode 100644 index 00000000..56413787 --- /dev/null +++ b/contracts/interfaces/IAuthValidator.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +/** + * @dev IAuthValidator. Interface for verification of auth data. + */ +interface IAuthValidator { + /** + * @dev AuthResponseField. 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 AuthResponseField { + 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 sender Sender of the proof. + * @param proof Proof packed as bytes to verify. + * @param params Request query data of the credential to verify. + * @return userID User Id for the auth proof verified and response fields. + * @return authResponseFields Additional response fields. + */ + function verify( + address sender, + bytes calldata proof, + bytes calldata params + ) external returns (uint256 userID, AuthResponseField[] memory authResponseFields); +} diff --git a/contracts/interfaces/ICircuitValidator.sol b/contracts/interfaces/ICircuitValidator.sol deleted file mode 100644 index 273172af..00000000 --- a/contracts/interfaces/ICircuitValidator.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {IState} from "./IState.sol"; - -/** - * @dev ICircuitValidator. Interface for circuit verification. - */ -interface ICircuitValidator { - /** - * @dev KeyToInputIndex. Information about public inputs of the circuit verification. Used in verify function. - * @param key Name of the public input - * @param inputIndex Index of the public input - * - * Note: Kept for backward compatibility. Now it's replaced by Signal struct for verifyV2 function. - */ - struct KeyToInputIndex { - string key; - uint256 inputIndex; - } - - /** - * @dev Signal. Information about public signals of the circuit verification. Used in verifyV2 function. - * @param name Name of the public signal - * @param value Value of the public signal - */ - struct Signal { - string name; - uint256 value; - } - - /** - * @dev Get version of the contract - */ - function version() external view returns (string memory); - - /** - * @dev Verify with the supported circuit informed in the request query data the groth16 proof - * π=([πa]1,[πb]2,[πc]1) and that the proof was generated by the sender. - * @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. - */ - function verify( - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - bytes calldata data, - address sender - ) external returns (ICircuitValidator.KeyToInputIndex[] memory); - - /** - * @dev Verify with the supported circuit informed in the request query data the groth16 proof - * packed as bytes and that the proof was generated by the sender. - * @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 state State contract to get identities and gist states to check. - * @return Array of public signals as result. - */ - function verifyV2( - bytes calldata zkProof, - bytes calldata data, - address sender, - IState state - ) external returns (ICircuitValidator.Signal[] memory); - - /** - * @dev Get supported circuit ids. - * @return ids Array of circuit ids supported. - */ - function getSupportedCircuitIds() external view returns (string[] memory ids); - - /** - * @dev Get the index of the public input of the circuit by name. - * @param name Name of the public input. - * @return Index of the public input. - */ - function inputIndexOf(string memory name) external view returns (uint256); -} diff --git a/contracts/interfaces/IGroth16Verifier.sol b/contracts/interfaces/IGroth16Verifier.sol new file mode 100644 index 00000000..e41c4196 --- /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 signals Public inputs and outputs 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 signals + ) external view returns (bool r); +} diff --git a/contracts/interfaces/IRequestValidator.sol b/contracts/interfaces/IRequestValidator.sol new file mode 100644 index 00000000..2d921eb9 --- /dev/null +++ b/contracts/interfaces/IRequestValidator.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +/** + * @dev IRequestValidator. Interface for verification of request query data. + */ +interface IRequestValidator { + /** + * @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 RequestParam. Information about request param from request query data. + * @param name Name of the request query param + * @param value Value of the request query param + */ + struct RequestParam { + 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 sender Sender of the proof. + * @param proof Proof packed as bytes to verify. + * @param params Request query data of the credential to verify. + * @return Array of response fields as result. + */ + function verify( + address sender, + bytes calldata proof, + bytes calldata params + ) external returns (ResponseField[] memory); + + /** + * @dev Get the request params of the request query data. + * @param params Request query data of the credential to verify. + * @return RequestParams of the request query data. + */ + function getRequestParams(bytes calldata params) external view returns (RequestParam[] memory); + + /** + * @dev Get the index of the request param by name + * @param name Name of the request param + * @return Index of the request param + */ + function requestParamIndexOf(string memory name) external view returns (uint256); +} diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index b7ba452f..fe8d9a52 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -1,22 +1,266 @@ // 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 creating requests and verifying request responses through 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 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; + } + + /** + * @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. + */ + struct RequestInfo { + uint256 requestId; + string metadata; + IRequestValidator validator; + bytes params; + address creator; + } + + /** + * @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 AuthResponse. Structure for auth response. + * @param authMethod Auth type of the proof response. + * @param proof proof to verify. + */ + struct AuthResponse { + string authMethod; + 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; + } + + /** + * @dev AuthMethod. Structure for auth type for auth proofs. + * @param authMethod Auth type of the auth proof. + * @param validator Validator to verify the auth. + * @param params Parameters data of the auth. + */ + struct AuthMethod { + string authMethod; + IAuthValidator validator; + bytes params; + } + + /** + * @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 MultiRequest { + uint256 multiRequestId; + uint256[] requestIds; + uint256[] groupIds; + bytes metadata; + } + + /** + * @dev Submits an array of responses and updates proofs status + * @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 authResponse, + Response[] memory responses, + bytes memory crossChainProofs + ) external; + + /** + * @dev Sets different requests + * @param requests List of requests + */ + function setRequests(Request[] calldata requests) 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 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 (RequestInfo[] memory); + + /** + * @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 Checks if a group ID exists + * @param groupId The ID of the group + * @return Whether the group ID exists + */ + function groupIdExists(uint256 groupId) external view returns (bool); + + /** + * @dev Checks if a multiRequest ID exists + * @param multiRequestId The ID of the multiRequest + * @return Whether the multiRequest ID exists + */ + function multiRequestIdExists(uint256 multiRequestId) external view returns (bool); + + /** + * @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 MultiRequest. "True" if all requests are verified, "false" otherwise + */ + function getMultiRequestProofsStatus( + uint256 multiRequestId, + address userAddress + ) external view returns (RequestProofStatus[] 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 areMultiRequestProofsVerified( + uint256 multiRequestId, + address userAddress + ) external view returns (bool); + + /** + * @dev Gets proof storage response field value + * @param requestId Id of the request + * @param sender Address of the user + * @param responseFieldName Name of the proof storage response field to get + */ + function getResponseFieldValue( + uint256 requestId, + address sender, + string memory responseFieldName + ) external view returns (uint256); + + /** + * @dev Gets proof storage response fields + * @param requestId Id of the request + * @param sender Address of the user + */ + function getResponseFields( + uint256 requestId, + address sender + ) external view returns (IRequestValidator.ResponseField[] memory); + + /** + * @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 isRequestProofVerified(address sender, uint256 requestId) external view returns (bool); + + /** + * @dev Sets an auth method + * @param authMethod The auth method to add + */ + function setAuthMethod(AuthMethod calldata authMethod) external; + + /** + * @dev Sets a multiRequest + * @param multiRequest The multiRequest data + */ + function setMultiRequest(MultiRequest calldata multiRequest) external; + + /** + * @dev Gets a specific multiRequest by ID + * @param multiRequestId The ID of the multiRequest + * @return multiRequest The multiRequest data + */ + function getMultiRequest( + uint256 multiRequestId + ) external view returns (MultiRequest memory multiRequest); + + /** + * @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 getRequestProofStatus( + address sender, + uint256 requestId + ) external view returns (RequestProofStatus memory); } 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/ArrayUtils.sol b/contracts/lib/ArrayUtils.sol index cb148fbb..60c6802b 100644 --- a/contracts/lib/ArrayUtils.sol +++ b/contracts/lib/ArrayUtils.sol @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; +error LenghtShouldBeGreaterThanZero(); +error LengthLimitExceeded(uint256 limit); +error StartIndexOutOfBounds(uint256 arrLength); + /// @title A common functions for arrays. library ArrayUtils { /** @@ -17,9 +21,15 @@ library ArrayUtils { uint256 length, uint256 limit ) internal pure returns (uint256, uint256) { - require(length > 0, "Length should be greater than 0"); - require(length <= limit, "Length limit exceeded"); - require(start < arrLength, "Start index out of bounds"); + if (length == 0) { + revert LenghtShouldBeGreaterThanZero(); + } + if (length > limit) { + revert LengthLimitExceeded(limit); + } + if (start >= arrLength) { + revert StartIndexOutOfBounds(arrLength); + } uint256 end = start + length; if (end > arrLength) { diff --git a/contracts/lib/ClaimBuilder.sol b/contracts/lib/ClaimBuilder.sol index bf8f5d31..bbb42773 100644 --- a/contracts/lib/ClaimBuilder.sol +++ b/contracts/lib/ClaimBuilder.sol @@ -3,6 +3,15 @@ pragma solidity 0.8.27; import {PrimitiveTypeUtils} from "../lib/PrimitiveTypeUtils.sol"; +error IdShouldBeEmpty(); +error IdShouldBeNotEmpty(); +error InvalidIdPosition(); +error RevocationNonceShouldBeZeroForNonExpirableClaim(); +error ExpirationDateShouldBeZeroForNonExpirableClaim(); +error VersionShouldBeZeroForNonUpdatableClaim(); +error DataSlotsShouldBeEmpty(); +error MerklizedRootShouldBeZeroForNonMerklizedClaim(); + library ClaimBuilder { // ID_POSITION_NONE means ID value not located in claim. uint8 public constant ID_POSITION_NONE = 0; @@ -50,6 +59,8 @@ library ClaimBuilder { uint256 valueDataSlotB; } + /* solhint-disable code-complexity */ + // RULE: each uint we convert to bytes has to be reversed (in go Little ending, solidity - big ending). // // Final result reverted bytes to get valid uint256 @@ -72,56 +83,64 @@ library ClaimBuilder { // ID if (c.idPosition == ID_POSITION_NONE) { - require(c.id == 0, "id should be empty"); + if (c.id != 0) { + revert IdShouldBeEmpty(); + } } else if (c.idPosition == ID_POSITION_INDEX) { - require(c.id != 0, "id should be not empty"); + if (c.id == 0) { + revert IdShouldBeNotEmpty(); + } flags |= SUBJECT_FLAG_OTHER_IDEN_INDEX; claim[1] = c.id; } else if (c.idPosition == ID_POSITION_VALUE) { - require(c.id != 0, "id should be not empty"); + if (c.id == 0) { + revert IdShouldBeNotEmpty(); + } flags |= SUBJECT_FLAG_OTHER_IDEN_VALUE; claim[5] = c.id; } else { - require(false, "invalid id position"); + revert InvalidIdPosition(); } // Expirable if (c.expirable) { flags |= EXPIRABLE_FLAG_YES; - } else { - require(c.expirationDate == 0, "expirationDate should be 0 for non expirable claim"); + } else if (c.expirationDate != 0) { + revert ExpirationDateShouldBeZeroForNonExpirableClaim(); } // Updatable if (c.updatable) { flags |= UPDATABLE_FLAG_YES; - } else { - require(c.version == 0, "version should be 0 for non updatable claim"); + } else if (c.version != 0) { + revert VersionShouldBeZeroForNonUpdatableClaim(); } // Merklized Root if (c.merklizedRootPosition == MERKLIZED_ROOT_POSITION_INDEX) { - require( - c.indexDataSlotA == 0 && - c.indexDataSlotB == 0 && - c.valueDataSlotA == 0 && - c.indexDataSlotB == 0, - "data slots should be empty" - ); + if ( + c.indexDataSlotA != 0 || + c.indexDataSlotB != 0 || + c.valueDataSlotA != 0 || + c.valueDataSlotB != 0 + ) { + revert DataSlotsShouldBeEmpty(); + } flags |= MERKLIZED_FLAG_INDEX; claim[2] = c.merklizedRoot; } else if (c.merklizedRootPosition == MERKLIZED_ROOT_POSITION_VALUE) { - require( - c.indexDataSlotA == 0 && - c.indexDataSlotB == 0 && - c.valueDataSlotA == 0 && - c.indexDataSlotB == 0, - "data slots should be empty" - ); + if ( + c.indexDataSlotA != 0 || + c.indexDataSlotB != 0 || + c.valueDataSlotA != 0 || + c.valueDataSlotB != 0 + ) { + revert DataSlotsShouldBeEmpty(); + } flags |= MERKLIZED_FLAG_VALUE; claim[6] = c.merklizedRoot; - } else { - require(c.merklizedRoot == 0, "merklizedRoot should be 0 for non merklized claim"); + } else if (c.merklizedRoot != 0) { + revert MerklizedRootShouldBeZeroForNonMerklizedClaim(); } bytes memory claim0 = PrimitiveTypeUtils.concat( @@ -153,4 +172,5 @@ library ClaimBuilder { return claim; } + /* solhint-enable code-complexity */ } diff --git a/contracts/lib/GenesisUtils.sol b/contracts/lib/GenesisUtils.sol index 8be58af7..888dc0f7 100644 --- a/contracts/lib/GenesisUtils.sol +++ b/contracts/lib/GenesisUtils.sol @@ -3,12 +3,15 @@ pragma solidity 0.8.27; import {PrimitiveTypeUtils} from "./PrimitiveTypeUtils.sol"; +error ChecksumLengthRequired(uint256 length); +error IdBytesLengthRequired(uint256 length); + library GenesisUtils { /** * @dev sum */ function sum(bytes memory array) internal pure returns (uint16 s) { - require(array.length == 29, "Checksum requires 29 length array"); + if (array.length != 29) revert ChecksumLengthRequired(29); for (uint256 i = 0; i < array.length; ++i) { s += uint16(uint8(array[i])); @@ -49,7 +52,7 @@ library GenesisUtils { bytes memory checkSumBytes = abi.encodePacked(checksum); bytes memory idBytes = PrimitiveTypeUtils.concat(beforeChecksum, checkSumBytes); - require(idBytes.length == 31, "idBytes requires 31 length array"); + if (idBytes.length != 31) revert IdBytesLengthRequired(31); return PrimitiveTypeUtils.reverseUint256(PrimitiveTypeUtils.padRightToUint256(idBytes)); } diff --git a/contracts/lib/IdentityBase.sol b/contracts/lib/IdentityBase.sol index 0ba9527f..3c5c8887 100644 --- a/contracts/lib/IdentityBase.sol +++ b/contracts/lib/IdentityBase.sol @@ -7,6 +7,8 @@ import {IdentityLib} from "../lib/IdentityLib.sol"; import {SmtLib} from "../lib/SmtLib.sol"; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +error IdentityIdMismatch(); + // /** // * @dev Contract managing onchain identity // */ @@ -20,11 +22,13 @@ abstract contract IdentityBase is IIdentifiable, IOnchainCredentialStatusResolve // keccak256(abi.encode(uint256(keccak256("iden3.storage.IdentityBase")) - 1)) // & ~bytes32(uint256(0xff)); + // solhint-disable-next-line const-name-snakecase bytes32 private constant IdentityBaseStorageLocation = 0x3018a310c36c4f8228f09bf3b1822685cf0971daa8265a58ca807c4a4daba400; /// @dev Get the main storage using assembly to ensure specific storage location function _getIdentityBaseStorage() internal pure returns (IdentityBaseStorage storage $) { + // solhint-disable-next-line no-inline-assembly assembly { $.slot := IdentityBaseStorageLocation } @@ -262,7 +266,9 @@ abstract contract IdentityBase is IIdentifiable, IOnchainCredentialStatusResolve uint256 state, uint64 nonce ) public view returns (CredentialStatus memory) { - require(id == _getIdentityBaseStorage().identity.id, "Identity id mismatch"); + if (id != _getIdentityBaseStorage().identity.id) { + revert IdentityIdMismatch(); + } IdentityLib.Roots memory historicalStates = _getIdentityBaseStorage() .identity .getRootsByState(state); diff --git a/contracts/lib/IdentityLib.sol b/contracts/lib/IdentityLib.sol index 1c4a2180..43774c1b 100644 --- a/contracts/lib/IdentityLib.sol +++ b/contracts/lib/IdentityLib.sol @@ -6,6 +6,12 @@ import {SmtLib} from "../lib/SmtLib.sol"; import {PoseidonUnit3L, PoseidonUnit4L} from "../lib/Poseidon.sol"; import {GenesisUtils} from "../lib/GenesisUtils.sol"; +error SMTDepthIsGreaterThanMaxAllowed(); +error IdTypeNotSupported(); +error IdentityTreesHaventChanged(); +error RootsForThisStateDoesntExist(); +error RootsForThisStateAlreadyExist(); + // /** // * @dev Contract managing onchain identity // */ @@ -81,9 +87,13 @@ library IdentityLib { uint256 depth, bytes2 idType ) external { - require(depth <= IDENTITY_MAX_SMT_DEPTH, "SMT depth is greater than max allowed depth"); + if (depth > IDENTITY_MAX_SMT_DEPTH) { + revert SMTDepthIsGreaterThanMaxAllowed(); + } self.stateContract = IState(_stateContractAddr); - require(self.stateContract.isIdTypeSupported(idType), "id type is not supported"); + if (!self.stateContract.isIdTypeSupported(idType)) { + revert IdTypeNotSupported(); + } self.isOldStateGenesis = true; self.trees.claimsTree.initialize(depth); self.trees.revocationsTree.initialize(depth); @@ -133,12 +143,13 @@ library IdentityLib { uint256 currentRevocationsTreeRoot = self.trees.revocationsTree.getRoot(); uint256 currentRootsTreeRoot = self.trees.rootsTree.getRoot(); - require( - (self.latestPublishedTreeRoots.claimsRoot != currentClaimsTreeRoot) || - (self.latestPublishedTreeRoots.revocationsRoot != currentRevocationsTreeRoot) || - (self.latestPublishedTreeRoots.rootsRoot != currentRootsTreeRoot), - "Identity trees haven't changed" - ); + if ( + (self.latestPublishedTreeRoots.claimsRoot == currentClaimsTreeRoot) && + (self.latestPublishedTreeRoots.revocationsRoot == currentRevocationsTreeRoot) && + (self.latestPublishedTreeRoots.rootsRoot == currentRootsTreeRoot) + ) { + revert IdentityTreesHaventChanged(); + } // if claimsTreeRoot changed, then add it to rootsTree if (self.latestPublishedTreeRoots.claimsRoot != currentClaimsTreeRoot) { @@ -384,22 +395,6 @@ library IdentityLib { return self.trees.rootsTree.getRoot(); } - /** - * @dev write roots to history by state - * @param self identity - * @param state identity state - * @param roots set of roots - */ - function writeHistory(Data storage self, uint256 state, Roots memory roots) internal { - require( - self.rootsByState[state].claimsRoot == 0 && - self.rootsByState[state].revocationsRoot == 0 && - self.rootsByState[state].rootsRoot == 0, - "Roots for this state already exist" - ); - self.rootsByState[state] = roots; - } - /** * @dev returns historical claimsTree roots, revocationsTree roots, rootsTree roots * by state @@ -410,12 +405,30 @@ library IdentityLib { Data storage self, uint256 state ) external view returns (Roots memory) { - require( - self.rootsByState[state].claimsRoot != 0 || - self.rootsByState[state].revocationsRoot != 0 || - self.rootsByState[state].rootsRoot != 0, - "Roots for this state doesn't exist" - ); + if ( + self.rootsByState[state].claimsRoot == 0 && + self.rootsByState[state].revocationsRoot == 0 && + self.rootsByState[state].rootsRoot == 0 + ) { + revert RootsForThisStateDoesntExist(); + } return self.rootsByState[state]; } + + /** + * @dev write roots to history by state + * @param self identity + * @param state identity state + * @param roots set of roots + */ + function writeHistory(Data storage self, uint256 state, Roots memory roots) internal { + if ( + self.rootsByState[state].claimsRoot != 0 || + self.rootsByState[state].revocationsRoot != 0 || + self.rootsByState[state].rootsRoot != 0 + ) { + revert RootsForThisStateAlreadyExist(); + } + self.rootsByState[state] = roots; + } } diff --git a/contracts/lib/Poseidon.sol b/contracts/lib/Poseidon.sol index c4b8f83c..2d26b964 100644 --- a/contracts/lib/Poseidon.sol +++ b/contracts/lib/Poseidon.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; +/* solhint-disable no-empty-blocks */ library PoseidonUnit1L { function poseidon(uint256[1] calldata) public pure returns (uint256) {} } @@ -24,6 +25,7 @@ library PoseidonUnit5L { library PoseidonUnit6L { function poseidon(uint256[6] calldata) public pure returns (uint256) {} } +/* solhint-enable no-empty-blocks */ library SpongePoseidon { uint32 internal constant BATCH_SIZE = 6; diff --git a/contracts/lib/PrimitiveTypeUtils.sol b/contracts/lib/PrimitiveTypeUtils.sol index 929077b9..f5c543a4 100644 --- a/contracts/lib/PrimitiveTypeUtils.sol +++ b/contracts/lib/PrimitiveTypeUtils.sol @@ -3,12 +3,15 @@ pragma solidity 0.8.27; import {BytesLib} from "solidity-bytes-utils/contracts/BytesLib.sol"; +error GivenInputNotAnAddressRepresentation(uint256 input); + library PrimitiveTypeUtils { /** * @dev uint256ToBytes */ function uint256ToBytes(uint256 x) internal pure returns (bytes memory b) { b = new bytes(32); + // solhint-disable-next-line no-inline-assembly assembly { mstore(add(b, 32), x) } @@ -90,6 +93,7 @@ library PrimitiveTypeUtils { * @dev bytesToAddress */ function bytesToAddress(bytes memory bys) internal pure returns (address addr) { + // solhint-disable-next-line no-inline-assembly assembly { addr := mload(add(bys, 20)) } @@ -135,10 +139,9 @@ library PrimitiveTypeUtils { * @return address representation of uint256 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 */ function uint256ToAddress(uint256 input) internal pure returns (address) { - require( - input == uint256(uint160(input)), - "given input is not a representation of address, 12 most significant bytes should be zero" - ); + if (input != uint256(uint160(input))) { + revert GivenInputNotAnAddressRepresentation(input); + } return address(uint160(input)); } @@ -166,10 +169,9 @@ library PrimitiveTypeUtils { * @return address - 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 */ function uint256LEToAddress(uint256 input) internal pure returns (address) { - require( - input == uint256(uint160(input)), - "given uint256 is not a representation of an address, 12 most significant bytes should be zero" - ); + if (input != uint256(uint160(input))) { + revert GivenInputNotAnAddressRepresentation(input); + } return bytesToAddress(uint256ToBytes(reverseUint256(input))); } diff --git a/contracts/lib/SmtLib.sol b/contracts/lib/SmtLib.sol index 809fb86c..048b2af1 100644 --- a/contracts/lib/SmtLib.sol +++ b/contracts/lib/SmtLib.sol @@ -4,6 +4,18 @@ pragma solidity 0.8.27; import {PoseidonUnit2L, PoseidonUnit3L} from "./Poseidon.sol"; import {ArrayUtils} from "./ArrayUtils.sol"; +error RootDoesNotExist(); +error InvalidNodeType(); +error NoFutureTimestampsAllowed(); +error NoFutureBlocksAllowed(); +error MaxDepthMustBeGreaterThanZero(); +error MaxDepthCanOnlyBeIncreased(); +error MaxDepthIsGreaterThanHardCap(); +error SmtAlreadyInitialized(); +error SmtNotInitialized(); +error MaxDepthReached(); +error InvalidSearchType(); + /// @title A sparse merkle tree implementation, which keeps tree history. // Note that this SMT implementation can manage duplicated roots in the history, // which may happen when some leaf change its value and then changes it back to the original value. @@ -132,7 +144,9 @@ library SmtLib { * @param root SMT root. */ modifier onlyExistingRoot(Data storage self, uint256 root) { - require(rootExists(self, root), "Root does not exist"); + if (!rootExists(self, root)) { + revert RootDoesNotExist(); + } _; } @@ -264,7 +278,7 @@ library SmtLib { proof.siblings[i] = node.childRight; } } else { - revert("Invalid node type"); + revert InvalidNodeType(); } } return proof; @@ -313,7 +327,9 @@ library SmtLib { Data storage self, uint256 timestamp ) public view returns (RootEntryInfo memory) { - require(timestamp <= block.timestamp, "No future timestamps allowed"); + if (timestamp > block.timestamp) { + revert NoFutureTimestampsAllowed(); + } return _getRootInfoByTimestampOrBlock( @@ -332,7 +348,9 @@ library SmtLib { Data storage self, uint256 blockN ) public view returns (RootEntryInfo memory) { - require(blockN <= block.number, "No future blocks allowed"); + if (blockN > block.number) { + revert NoFutureBlocksAllowed(); + } return _getRootInfoByTimestampOrBlock(self, blockN, BinarySearchSmtRoots.SearchType.BLOCK); } @@ -408,9 +426,15 @@ library SmtLib { * @param maxDepth max depth */ function setMaxDepth(Data storage self, uint256 maxDepth) public { - require(maxDepth > 0, "Max depth must be greater than zero"); - require(maxDepth > self.maxDepth, "Max depth can only be increased"); - require(maxDepth <= MAX_DEPTH_HARD_CAP, "Max depth is greater than hard cap"); + if (maxDepth == 0) { + revert MaxDepthMustBeGreaterThanZero(); + } + if (maxDepth <= self.maxDepth) { + revert MaxDepthCanOnlyBeIncreased(); + } + if (maxDepth > MAX_DEPTH_HARD_CAP) { + revert MaxDepthIsGreaterThanHardCap(); + } self.maxDepth = maxDepth; } @@ -427,14 +451,18 @@ library SmtLib { * @param maxDepth Max depth of the SMT. */ function initialize(Data storage self, uint256 maxDepth) external { - require(!isInitialized(self), "Smt is already initialized"); + if (isInitialized(self)) { + revert SmtAlreadyInitialized(); + } setMaxDepth(self, maxDepth); _addEntry(self, 0, 0, 0); self.initialized = true; } modifier onlyInitialized(Data storage self) { - require(isInitialized(self), "Smt is not initialized"); + if (!isInitialized(self)) { + revert SmtNotInitialized(); + } _; } @@ -449,7 +477,7 @@ library SmtLib { uint256 depth ) internal returns (uint256) { if (depth > self.maxDepth) { - revert("Max depth reached"); + revert MaxDepthReached(); } Node memory node = self.nodes[nodeHash]; @@ -502,7 +530,7 @@ library SmtLib { // no reason to continue if we are at max possible depth // as, anyway, we exceed the depth going down the tree if (depth >= self.maxDepth) { - revert("Max depth reached"); + revert MaxDepthReached(); } Node memory newNodeMiddle; @@ -696,7 +724,7 @@ library BinarySearchSmtRoots { } else if (st == SearchType.TIMESTAMP) { return rti.createdAtTimestamp; } else { - revert("Invalid search type"); + revert InvalidSearchType(); } } } diff --git a/contracts/lib/StateCrossChainLib.sol b/contracts/lib/StateCrossChainLib.sol deleted file mode 100644 index 4292ab0f..00000000 --- a/contracts/lib/StateCrossChainLib.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {IState} from "../interfaces/IState.sol"; -import {State} from "../state/State.sol"; - -/** - * @title StateCrossChainLib - * @dev The library provides functions to process cross chain proofs. - */ -library StateCrossChainLib { - bytes32 private constant GLOBAL_STATE_PROOF_TYPE = keccak256(bytes("globalStateProof")); - - bytes32 private constant STATE_PROOF_TYPE = keccak256(bytes("stateProof")); - - /** - * @dev Processes cross chain proofs. - * @param self The StateCrossChainStorage storage pointer. - * @param crossChainProofs The cross chain proofs. - */ - function processCrossChainProofs( - State.StateCrossChainStorage storage self, - bytes calldata crossChainProofs - ) public { - if (crossChainProofs.length == 0) { - return; - } - - IState.CrossChainProof[] memory proofs = abi.decode( - crossChainProofs, - (IState.CrossChainProof[]) - ); - - for (uint256 i = 0; i < proofs.length; i++) { - if (keccak256(bytes(proofs[i].proofType)) == GLOBAL_STATE_PROOF_TYPE) { - IState.GlobalStateProcessResult memory gsp = self - ._crossChainProofValidator - .processGlobalStateProof(proofs[i].proof); - self._rootToGistRootReplacedAt[gsp.idType][gsp.root] = gsp.replacedAtTimestamp; - } else if (keccak256(bytes(proofs[i].proofType)) == STATE_PROOF_TYPE) { - IState.IdentityStateProcessResult memory isu = self - ._crossChainProofValidator - .processIdentityStateProof(proofs[i].proof); - self._idToStateReplacedAt[isu.id][isu.state] = isu.replacedAtTimestamp; - } else { - revert("Unknown proof type"); - } - } - } -} diff --git a/contracts/lib/StateLib.sol b/contracts/lib/StateLib.sol index cf64be95..6434df99 100644 --- a/contracts/lib/StateLib.sol +++ b/contracts/lib/StateLib.sol @@ -3,6 +3,10 @@ pragma solidity 0.8.27; import {ArrayUtils} from "../lib/ArrayUtils.sol"; +error IdentityDoesNotExist(); +error StateDoesNotExist(); +error IdentityAlreadyExists(); + /// @title Library for state data management. // It's purpose is to keep records of identity states along with their metadata and history. library StateLib { @@ -91,7 +95,9 @@ library StateLib { * @param id Identity */ modifier onlyExistingId(Data storage self, uint256 id) { - require(idExists(self, id), "Identity does not exist"); + if (!idExists(self, id)) { + revert IdentityDoesNotExist(); + } _; } @@ -101,7 +107,9 @@ library StateLib { * @param state State */ modifier onlyExistingState(Data storage self, uint256 id, uint256 state) { - require(stateExists(self, id, state), "State does not exist"); + if (!stateExists(self, id, state)) { + revert StateDoesNotExist(); + } _; } @@ -120,10 +128,9 @@ library StateLib { * @param state State */ function addGenesisState(Data storage self, uint256 id, uint256 state) external { - require( - !idExists(self, id), - "Zero timestamp and block should be only in the first identity state" - ); + if (idExists(self, id)) { + revert IdentityAlreadyExists(); + } _addState(self, id, state, 0, 0); } diff --git a/contracts/lib/VerifierLib.sol b/contracts/lib/VerifierLib.sol deleted file mode 100644 index a47ad53d..00000000 --- a/contracts/lib/VerifierLib.sol +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {ZKPVerifierBase} from "../verifiers/ZKPVerifierBase.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; - -/** - * @title VerifierLib - * @dev A library for writing proof results. - */ -library VerifierLib { - /// @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 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 - */ - function writeProofResults( - ZKPVerifierBase.ZKPVerifierStorage storage self, - address sender, - uint64 requestId, - ICircuitValidator.KeyToInputIndex[] memory keyToInpIdxs, - uint256[] memory inputs - ) 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.isVerified = true; - proof.validatorVersion = self._requests[requestId].validator.version(); - proof.blockNumber = block.number; - proof.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 - */ - function writeProofResultsV2( - ZKPVerifierBase.ZKPVerifierStorage storage self, - address sender, - uint64 requestId, - ICircuitValidator.Signal[] memory signals - ) public { - Proof storage proof = self._proofs[sender][requestId]; - for (uint256 i = 0; i < signals.length; i++) { - proof.storageFields[signals[i].name] = signals[i].value; - } - - proof.isVerified = true; - proof.validatorVersion = self._requests[requestId].validator.version(); - proof.blockNumber = block.number; - proof.blockTimestamp = block.timestamp; - } -} diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierAuthV2Wrapper.sol b/contracts/lib/groth16-verifiers/Groth16VerifierAuthV2Wrapper.sol index c97d1e64..89d092f8 100644 --- a/contracts/lib/groth16-verifiers/Groth16VerifierAuthV2Wrapper.sol +++ b/contracts/lib/groth16-verifiers/Groth16VerifierAuthV2Wrapper.sol @@ -1,32 +1,12 @@ -// -// Copyright 2017 Christian Reitwiessner -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom -// the Software is furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// 2019 OKIMS -// ported to solidity 0.6 -// fixed linter warnings -// added requiere error messages -// -// // SPDX-License-Identifier: GPL-3.0 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 { +error ExpectedArrayLenght(uint256 expected, uint256 actual); + +contract Groth16VerifierAuthV2Wrapper is Groth16VerifierAuthV2, IGroth16Verifier { /** * @dev Number of public signals for atomic mtp circuit */ @@ -37,21 +17,23 @@ contract Groth16VerifierAuthV2Wrapper is Groth16VerifierAuthV2, IVerifier { * @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. + * @param signals Public inputs and outputs of the circuit. * @return r true if the proof is valid. */ function verify( uint256[2] calldata a, uint256[2][2] calldata b, uint256[2] calldata c, - uint256[] calldata input + uint256[] calldata signals ) public view returns (bool r) { uint[PUBSIGNALS_LENGTH] memory pubSignals; - require(input.length == PUBSIGNALS_LENGTH, "expected array length is 3"); + if (signals.length != PUBSIGNALS_LENGTH) { + revert ExpectedArrayLenght(PUBSIGNALS_LENGTH, signals.length); + } for (uint256 i = 0; i < PUBSIGNALS_LENGTH; i++) { - pubSignals[i] = input[i]; + pubSignals[i] = signals[i]; } return this.verifyProof(a, b, c, pubSignals); diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierLinkedMultiQuery10.sol b/contracts/lib/groth16-verifiers/Groth16VerifierLinkedMultiQuery10.sol new file mode 100644 index 00000000..e2dba6a7 --- /dev/null +++ b/contracts/lib/groth16-verifiers/Groth16VerifierLinkedMultiQuery10.sol @@ -0,0 +1,376 @@ +// 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 = + 4767756989901781990548811495555254021246964220885607355087778530306964004185; + uint256 constant deltax2 = + 13574129252125202270158307841999632289188481698271261939235393046564697547323; + uint256 constant deltay1 = + 11765493019200775638510616767809212227835759957683292721283970143875449362878; + uint256 constant deltay2 = + 20994491963918641855107870498261517959074286551785099903933200630273567734342; + + uint256 constant IC0x = + 12449886944142661472251059230965936349361235560943050777171852750972268056009; + uint256 constant IC0y = + 5508789765348348011558576931625983265119736287691184278767289209256184182152; + + uint256 constant IC1x = + 11354840822823409678846005531170154810223893374328536931571598076429246168962; + uint256 constant IC1y = + 10243618321308788660349859395450060514823490518996600371300209313988271557806; + + uint256 constant IC2x = + 7688841796121824588147585218713985820690021917094358723669767856390683928034; + uint256 constant IC2y = + 15304029843415543132293541704424359450862448668530347048215672038580045683481; + + uint256 constant IC3x = + 15990615657429515286876718277658019436828926204319889565777537283744068146700; + uint256 constant IC3y = + 20265128390631794181627612941990068143695235211256419586038084564697570772459; + + uint256 constant IC4x = + 16744634382041772612761056860980716914432614100602561913600347639646803828867; + uint256 constant IC4y = + 9587909504738762931618416803620503763808524690654300610728307244650105345649; + + uint256 constant IC5x = + 14498281259442737211687928465501452644380043044531604747511002130574576040500; + uint256 constant IC5y = + 15178480169883279183083105005735414370343021495727319036601387862295433592890; + + uint256 constant IC6x = + 181238700928172282344680236185737466543303371505875966182504955165221550787; + uint256 constant IC6y = + 21622111637396317730948136644302630767714713068723532481132071344883159478317; + + uint256 constant IC7x = + 8334117149439267478794081283986502998659211363774660979410854038645857015106; + uint256 constant IC7y = + 3525586056545466424550069059261704178677653029630068235241952571550605630653; + + uint256 constant IC8x = + 16676389244152071637547895889424069474191043909363062157910970100503924284824; + uint256 constant IC8y = + 6293986690744783536251466348123663316687870380690807594123241364218706730246; + + uint256 constant IC9x = + 12745671365224662776594194440156600675329812876347548121369698185420569095439; + uint256 constant IC9y = + 11273088596548493444123027464142605382437970433270231501917065525393005894036; + + uint256 constant IC10x = + 7732485931307476787148144929824683305147104743163709148772223736952704050567; + uint256 constant IC10y = + 14991775678419768558530779568394256066852823412993601432448554479361118463299; + + uint256 constant IC11x = + 13954475229491875183185146721491133006576631796979640931033593718558384269206; + uint256 constant IC11y = + 20143678799568261548345812147552378448221261337943896478291695109662795302646; + + uint256 constant IC12x = + 1588536655220107824895151554872386730171641945783207210783928981583577082720; + uint256 constant IC12y = + 13908530648827733472139197820866316501402019214593222521521102979981263265396; + + uint256 constant IC13x = + 12678767645933368864421466910761496605084347784517452696623065956846509548782; + uint256 constant IC13y = + 21381570127686765465000169852593021495333227087229864265691446720659272361152; + + uint256 constant IC14x = + 17922265673268483320025865036589139344955822363275373430719168065953761526520; + uint256 constant IC14y = + 9242324301503892823219332201525279187476010610994752688104429744801597668285; + + uint256 constant IC15x = + 19367539127735956732148435844861647320899694335953718141209016532640873590140; + uint256 constant IC15y = + 12701104584447112200166345844417732176637947754547635778619790266357846083284; + + uint256 constant IC16x = + 14931750548482966130586321361300230947899794584196248761236252137274123990811; + uint256 constant IC16y = + 18907870831743031028168656813690968152456035625888662633278498386598866738708; + + uint256 constant IC17x = + 21078326524345796712273699205406122410330437647297400186087773951320605894880; + uint256 constant IC17y = + 6471701510558433137588469036231611931381433511837825536013781894924055589201; + + uint256 constant IC18x = + 11616604898621091236885062107603844843578912315924240360909152763967953411071; + uint256 constant IC18y = + 15567597962932438133376009485279673723080736998791665521084155531250437535832; + + uint256 constant IC19x = + 16814378820042549514945932350142180953549065761435730844701764513083012014298; + uint256 constant IC19y = + 9577851565990440995137571478255586121135591079059958395444685890902579770570; + + uint256 constant IC20x = + 1093702848180480792269642835164492672016092778979951861285096707497432193760; + uint256 constant IC20y = + 4063334433551442475817332481927046015343707417264061346417488535608502495218; + + uint256 constant IC21x = + 7214731470556020664921656204545020072837783006969685612760693537299230135333; + uint256 constant IC21y = + 8891562787830667150144624187125115054175583159717508708300614390764766181778; + + uint256 constant IC22x = + 4041991063957841891847968885939221032895793579852508335899469034278358488695; + uint256 constant IC22y = + 12929528695870206289536816082066059156374288392417066761527212742555189041207; + + // 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, r)) { + 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))) + + // Validate all evaluations + let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem) + + mstore(0, isValid) + return(0, 0x20) + } + } +} diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierLinkedMultiQuery10Wrapper.sol b/contracts/lib/groth16-verifiers/Groth16VerifierLinkedMultiQuery10Wrapper.sol new file mode 100644 index 00000000..0c611638 --- /dev/null +++ b/contracts/lib/groth16-verifiers/Groth16VerifierLinkedMultiQuery10Wrapper.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.10; + +import {IGroth16Verifier} from "../../interfaces/IGroth16Verifier.sol"; +import {Groth16VerifierLinkedMultiQuery10} from "./Groth16VerifierLinkedMultiQuery10.sol"; + +error ExpectedArrayLenght(uint256 expected, uint256 actual); + +contract Groth16VerifierLinkedMultiQuery10Wrapper is + Groth16VerifierLinkedMultiQuery10, + IGroth16Verifier +{ + /** + * @dev Number of public signals for atomic mtp circuit + */ + uint256 constant PUBSIGNALS_LENGTH = 22; + + /** + * @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 signals Public inputs and outputs of the circuit. + * @return r true if the proof is valid. + */ + function verify( + uint256[2] calldata a, + uint256[2][2] calldata b, + uint256[2] calldata c, + uint256[] calldata signals + ) public view returns (bool r) { + uint[PUBSIGNALS_LENGTH] memory pubSignals; + + if (signals.length != PUBSIGNALS_LENGTH) { + revert ExpectedArrayLenght(PUBSIGNALS_LENGTH, signals.length); + } + + for (uint256 i = 0; i < PUBSIGNALS_LENGTH; i++) { + pubSignals[i] = signals[i]; + } + + return this.verifyProof(a, b, c, pubSignals); + } +} 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/Groth16VerifierMTPWrapper.sol b/contracts/lib/groth16-verifiers/Groth16VerifierMTPWrapper.sol index 19b268de..8868f176 100644 --- a/contracts/lib/groth16-verifiers/Groth16VerifierMTPWrapper.sol +++ b/contracts/lib/groth16-verifiers/Groth16VerifierMTPWrapper.sol @@ -14,34 +14,38 @@ pragma solidity 0.8.27; import "./Groth16VerifierMTP.sol"; -import "../../interfaces/IVerifier.sol"; +import "../../interfaces/IGroth16Verifier.sol"; -contract Groth16VerifierMTPWrapper is Groth16VerifierMTP, IVerifier { +error ExpectedArrayLenght(uint256 expected, uint256 actual); + +contract Groth16VerifierMTPWrapper is Groth16VerifierMTP, IGroth16Verifier { /** * @dev Number of public signals for atomic mtp circuit */ - uint constant PUBSIGNALS_LENGTH = 11; + uint256 constant PUBSIGNALS_LENGTH = 11; /** * @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. + * @param signals Public inputs and outputs of the circuit. * @return r true if the proof is valid. */ function verify( uint256[2] calldata a, uint256[2][2] calldata b, uint256[2] calldata c, - uint256[] calldata input + uint256[] calldata signals ) public view returns (bool r) { uint[PUBSIGNALS_LENGTH] memory pubSignals; - require(input.length == PUBSIGNALS_LENGTH, "expected array length is 11"); + if (signals.length != PUBSIGNALS_LENGTH) { + revert ExpectedArrayLenght(PUBSIGNALS_LENGTH, signals.length); + } for (uint256 i = 0; i < PUBSIGNALS_LENGTH; i++) { - pubSignals[i] = input[i]; + pubSignals[i] = signals[i]; } return this.verifyProof(a, b, c, pubSignals); 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/Groth16VerifierSigWrapper.sol b/contracts/lib/groth16-verifiers/Groth16VerifierSigWrapper.sol index efd6665a..f65cbae7 100644 --- a/contracts/lib/groth16-verifiers/Groth16VerifierSigWrapper.sol +++ b/contracts/lib/groth16-verifiers/Groth16VerifierSigWrapper.sol @@ -14,35 +14,39 @@ pragma solidity 0.8.27; import "./Groth16VerifierSig.sol"; -import "../../interfaces/IVerifier.sol"; +import "../../interfaces/IGroth16Verifier.sol"; -contract Groth16VerifierSigWrapper is Groth16VerifierSig, IVerifier { +error ExpectedArrayLenght(uint256 expected, uint256 actual); + +contract Groth16VerifierSigWrapper is Groth16VerifierSig, IGroth16Verifier { /** * @dev Number of public signals for atomic sig circuit */ - uint constant PUBSIGNALS_LENGTH = 11; + uint256 constant PUBSIGNALS_LENGTH = 11; /** * @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. + * @param signals Public inputs and outputs of the circuit. * @return r true if the proof is valid. */ function verify( uint256[2] calldata a, uint256[2][2] calldata b, uint256[2] calldata c, - uint256[] calldata input + uint256[] calldata signals ) public view returns (bool r) { // slither-disable-next-line uninitialized-local uint[PUBSIGNALS_LENGTH] memory pubSignals; - require(input.length == PUBSIGNALS_LENGTH, "expected array length is 11"); + if (signals.length != PUBSIGNALS_LENGTH) { + revert ExpectedArrayLenght(PUBSIGNALS_LENGTH, signals.length); + } for (uint256 i = 0; i < PUBSIGNALS_LENGTH; i++) { - pubSignals[i] = input[i]; + pubSignals[i] = signals[i]; } return this.verifyProof(a, b, c, pubSignals); } 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/lib/groth16-verifiers/Groth16VerifierV3Wrapper.sol b/contracts/lib/groth16-verifiers/Groth16VerifierV3Wrapper.sol index 9f963726..e256652b 100644 --- a/contracts/lib/groth16-verifiers/Groth16VerifierV3Wrapper.sol +++ b/contracts/lib/groth16-verifiers/Groth16VerifierV3Wrapper.sol @@ -14,35 +14,39 @@ pragma solidity 0.8.27; import "./Groth16VerifierV3.sol"; -import "../../interfaces/IVerifier.sol"; +import "../../interfaces/IGroth16Verifier.sol"; -contract Groth16VerifierV3Wrapper is Groth16VerifierV3, IVerifier { +error ExpectedArrayLenght(uint256 expected, uint256 actual); + +contract Groth16VerifierV3Wrapper is Groth16VerifierV3, IGroth16Verifier { /** * @dev Number of public signals for atomic V3 circuit */ - uint constant PUBSIGNALS_LENGTH = 14; + uint256 constant PUBSIGNALS_LENGTH = 14; /** * @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. + * @param signals Public inputs and outputs of the circuit. * @return r true if the proof is valid. */ function verify( uint256[2] calldata a, uint256[2][2] calldata b, uint256[2] calldata c, - uint256[] calldata input + uint256[] calldata signals ) public view returns (bool r) { // slither-disable-next-line uninitialized-local uint[PUBSIGNALS_LENGTH] memory pubSignals; - require(input.length == PUBSIGNALS_LENGTH, "expected array length is 14"); + if (signals.length != PUBSIGNALS_LENGTH) { + revert ExpectedArrayLenght(PUBSIGNALS_LENGTH, signals.length); + } for (uint256 i = 0; i < PUBSIGNALS_LENGTH; i++) { - pubSignals[i] = input[i]; + pubSignals[i] = signals[i]; } return this.verifyProof(a, b, c, pubSignals); } diff --git a/contracts/package.json b/contracts/package.json index dfbf225c..f785351e 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,7 +1,7 @@ { "name": "@iden3/contracts", "description": "Smart Contract library for Solidity", - "version": "2.5.7", + "version": "3.0.0", "files": [ "**/*.sol", "/build/contracts/*.json", diff --git a/contracts/payment/MCPayment.sol b/contracts/payment/MCPayment.sol index 5c079efc..2eecb127 100644 --- a/contracts/payment/MCPayment.sol +++ b/contracts/payment/MCPayment.sol @@ -70,10 +70,12 @@ contract MCPayment is Ownable2StepUpgradeable, EIP712Upgradeable { // keccak256(abi.encode(uint256(keccak256("iden3.storage.MCPayment")) - 1)) & // ~bytes32(uint256(0xff)); + // solhint-disable-next-line const-name-snakecase bytes32 private constant MCPaymentStorageLocation = 0x843c93f996398391e581389b674681e6ea27a4f9a96390a9d8ecb41cf0226300; function _getMCPaymentStorage() private pure returns (MCPaymentStorage storage $) { + // solhint-disable-next-line no-inline-assembly assembly { $.slot := MCPaymentStorageLocation } @@ -222,6 +224,7 @@ contract MCPayment is Ownable2StepUpgradeable, EIP712Upgradeable { // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly + // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(permitSignature, 0x20)) s := mload(add(permitSignature, 0x40)) @@ -358,7 +361,7 @@ contract MCPayment is Ownable2StepUpgradeable, EIP712Upgradeable { * @dev Withdraw ERC-20 balance to owner */ function ownerERC20Withdraw(address token) public onlyOwner { - uint amount = IERC20(token).balanceOf(address(this)); + uint256 amount = IERC20(token).balanceOf(address(this)); if (amount == 0) { revert WithdrawErrorNoBalance(); } @@ -424,7 +427,7 @@ contract MCPayment is Ownable2StepUpgradeable, EIP712Upgradeable { _withdraw(amount, issuer); } - function _withdraw(uint amount, address to) internal { + function _withdraw(uint256 amount, address to) internal { if (to == address(0)) { revert WithdrawErrorInvalidAddress(); } diff --git a/contracts/payment/VCPayment.sol b/contracts/payment/VCPayment.sol index c645e0f3..5922f9a7 100644 --- a/contracts/payment/VCPayment.sol +++ b/contracts/payment/VCPayment.sol @@ -47,10 +47,12 @@ contract VCPayment is Ownable2StepUpgradeable { // keccak256(abi.encode(uint256(keccak256("iden3.storage.VCPayment")) - 1)) & // ~bytes32(uint256(0xff)); + // solhint-disable-next-line const-name-snakecase bytes32 private constant VCPaymentStorageLocation = 0xbb49acb92ce91902600caabfefad66ed7ac2a150edbd631ab48a5501402b3300; function _getVCPaymentStorage() private pure returns (VCPaymentStorage storage $) { + // solhint-disable-next-line no-inline-assembly assembly { $.slot := VCPaymentStorageLocation } @@ -245,7 +247,7 @@ contract VCPayment is Ownable2StepUpgradeable { _withdraw(amount, issuer); } - function _withdraw(uint amount, address to) internal { + function _withdraw(uint256 amount, address to) internal { if (amount == 0) { revert WithdrawError("There is no balance to withdraw"); } diff --git a/contracts/state/State.sol b/contracts/state/State.sol index dbb6f653..3017b1c9 100644 --- a/contracts/state/State.sol +++ b/contracts/state/State.sol @@ -7,16 +7,43 @@ import {IStateTransitionVerifier} from "../interfaces/IStateTransitionVerifier.s import {SmtLib} from "../lib/SmtLib.sol"; import {PoseidonUnit1L} from "../lib/Poseidon.sol"; import {StateLib} from "../lib/StateLib.sol"; -import {StateCrossChainLib} from "../lib/StateCrossChainLib.sol"; import {GenesisUtils} from "../lib/GenesisUtils.sol"; import {ICrossChainProofValidator} from "../interfaces/ICrossChainProofValidator.sol"; +error VerifierContractAddressShouldNotBeZero(); +error UnknownProofType(); +error DefaultIdTypeNotInitialized(); +error ZeroKnowledgeProofOfStateTransitionIsNotValid(); +error SenderIsNotIdentityOwner(); +error MethodParamsShouldBeEmpty(); +error OldStateShouldBeZero(); +error UnknownStateTransitionMethodId(); +error StateEntryNotFound(); +error CrossChainStateNotFound(); +error GistRootEntryNotFound(); +error CrossChainGistRootNotFound(); +error IdTypeNotSupported(); +error IdShouldNotBeZero(); +error NewStateShouldNotBeZero(); +error OldStateIsGenesisButIdentityAlreadyExists(); +error OldStateIsNotGenesisButIdentityDoesNotExist(); +error OldStateDoesNotMatchTheLatestState(); +error NewStateAlreadyExists(); + /// @title Set and get states for each identity contract State is Ownable2StepUpgradeable, IState { /** * @dev Version of contract */ - string public constant VERSION = "2.6.1"; + string public constant VERSION = "2.6.2"; + /** + * @dev Global state proof type + */ + bytes32 private constant GLOBAL_STATE_PROOF_TYPE = keccak256(bytes("globalStateProof")); + /** + * @dev State proof type + */ + bytes32 private constant STATE_PROOF_TYPE = keccak256(bytes("stateProof")); // This empty reserved space is put in place to allow future versions // of the State contract to inherit from other contracts without a risk of @@ -53,34 +80,34 @@ contract State is Ownable2StepUpgradeable, IState { */ bool internal _defaultIdTypeInitialized; - // keccak256(abi.encode(uint256(keccak256("iden3.storage.StateCrossChain")) - 1)) - // & ~bytes32(uint256(0xff)); - bytes32 private constant StateCrossChainStorageLocation = - 0xfe6de916382846695d2555237dc6c0ef6555f4c949d4ba263e03532600778100; - /// @custom:storage-location erc7201:iden3.storage.StateCrossChain struct StateCrossChainStorage { mapping(uint256 id => mapping(uint256 state => uint256 replacedAt)) _idToStateReplacedAt; mapping(bytes2 idType => mapping(uint256 root => uint256 replacedAt)) _rootToGistRootReplacedAt; ICrossChainProofValidator _crossChainProofValidator; - IState _state; + } + + // keccak256(abi.encode(uint256(keccak256("iden3.storage.StateCrossChain")) - 1)) + // & ~bytes32(uint256(0xff)); + // solhint-disable-next-line const-name-snakecase + bytes32 private constant StateCrossChainStorageLocation = + 0xfe6de916382846695d2555237dc6c0ef6555f4c949d4ba263e03532600778100; + + function _getStateCrossChainStorage() private pure returns (StateCrossChainStorage storage $) { + // solhint-disable-next-line no-inline-assembly + assembly { + $.slot := StateCrossChainStorageLocation + } } using SmtLib for SmtLib.Data; using StateLib for StateLib.Data; - using StateCrossChainLib for StateCrossChainStorage; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } - function _getStateCrossChainStorage() private pure returns (StateCrossChainStorage storage $) { - assembly { - $.slot := StateCrossChainStorageLocation - } - } - /** * @dev Initialize the contract * @param verifierContractAddr Verifier address @@ -99,7 +126,7 @@ contract State is Ownable2StepUpgradeable, IState { } if (address(verifierContractAddr) == address(0)) { - revert("Verifier contract address should not be zero"); + revert VerifierContractAddressShouldNotBeZero(); } verifier = verifierContractAddr; @@ -119,12 +146,35 @@ contract State is Ownable2StepUpgradeable, IState { } /** - * @dev Process cross chain proofs with identity and global state proofs - * @param proofs Cross chain proofs to be processed + * @dev Processes cross chain proofs. + * @param crossChainProofs The cross chain proofs. */ - function processCrossChainProofs(bytes calldata proofs) public { + function processCrossChainProofs(bytes calldata crossChainProofs) public { + if (crossChainProofs.length == 0) { + return; + } + + IState.CrossChainProof[] memory proofs = abi.decode( + crossChainProofs, + (IState.CrossChainProof[]) + ); + StateCrossChainStorage storage $ = _getStateCrossChainStorage(); - $.processCrossChainProofs(proofs); + for (uint256 i = 0; i < proofs.length; i++) { + if (keccak256(bytes(proofs[i].proofType)) == GLOBAL_STATE_PROOF_TYPE) { + IState.GlobalStateProcessResult memory gsp = $ + ._crossChainProofValidator + .processGlobalStateProof(proofs[i].proof); + $._rootToGistRootReplacedAt[gsp.idType][gsp.root] = gsp.replacedAtTimestamp; + } else if (keccak256(bytes(proofs[i].proofType)) == STATE_PROOF_TYPE) { + IState.IdentityStateProcessResult memory isu = $ + ._crossChainProofValidator + .processIdentityStateProof(proofs[i].proof); + $._idToStateReplacedAt[isu.id][isu.state] = isu.replacedAtTimestamp; + } else { + revert UnknownProofType(); + } + } } /** @@ -140,7 +190,9 @@ contract State is Ownable2StepUpgradeable, IState { * @return defaultIdType */ function getDefaultIdType() public view returns (bytes2) { - require(_defaultIdTypeInitialized, "Default Id Type is not initialized"); + if (!_defaultIdTypeInitialized) { + revert DefaultIdTypeNotInitialized(); + } return _defaultIdType; } @@ -174,10 +226,9 @@ contract State is Ownable2StepUpgradeable, IState { // Check if the id type is supported getIdTypeIfSupported(id); uint256[4] memory input = [id, oldState, newState, uint256(isOldStateGenesis ? 1 : 0)]; - require( - verifier.verifyProof(a, b, c, input), - "Zero-knowledge proof of state transition is not valid" - ); + if (!verifier.verifyProof(a, b, c, input)) { + revert ZeroKnowledgeProofOfStateTransitionIsNotValid(); + } _transitState(id, oldState, newState, isOldStateGenesis); } @@ -202,16 +253,22 @@ contract State is Ownable2StepUpgradeable, IState { bytes2 idType = getIdTypeIfSupported(id); if (methodId == 1) { uint256 calcId = GenesisUtils.calcIdFromEthAddress(idType, msg.sender); - require(calcId == id, "msg.sender is not owner of the identity"); - require(methodParams.length == 0, "methodParams should be empty"); + if (calcId != id) { + revert SenderIsNotIdentityOwner(); + } + if (methodParams.length != 0) { + revert MethodParamsShouldBeEmpty(); + } if (isOldStateGenesis) { - require(oldState == 0, "Old state should be zero"); + if (oldState != 0) { + revert OldStateShouldBeZero(); + } } _transitState(id, oldState, newState, isOldStateGenesis); } else { - revert("Unknown state transition method id"); + revert UnknownStateTransitionMethodId(); } } @@ -446,14 +503,14 @@ contract State is Ownable2StepUpgradeable, IState { } else if (GenesisUtils.isGenesisState(id, state)) { return 0; } - revert("State entry not found"); + revert StateEntryNotFound(); } else { StateCrossChainStorage storage $ = _getStateCrossChainStorage(); uint256 replacedAt = $._idToStateReplacedAt[id][state]; if (replacedAt != 0) { return replacedAt; } - revert("Cross-chain state not found"); + revert CrossChainStateNotFound(); } } @@ -468,17 +525,39 @@ contract State is Ownable2StepUpgradeable, IState { if (_gistData.rootExists(root)) { return _gistData.getRootInfo(root).replacedAtTimestamp; } - revert("GIST root entry not found"); + revert GistRootEntryNotFound(); } else { StateCrossChainStorage storage $ = _getStateCrossChainStorage(); uint256 replacedAt = $._rootToGistRootReplacedAt[idType][root]; if (replacedAt != 0) { return replacedAt; } - revert("Cross-chain GIST root not found"); + revert CrossChainGistRootNotFound(); } } + /** + * @dev Check if the id type is supported and return the id type + * @param id Identity + * trows if id type is not supported + */ + function getIdTypeIfSupported(uint256 id) public view returns (bytes2) { + bytes2 idType = GenesisUtils.getIdType(id); + if (!_stateData.isIdTypeSupported[idType]) { + revert IdTypeNotSupported(); + } + return idType; + } + + /** + * @dev Set supported IdType setter + * @param idType id type + * @param supported ability to enable or disable id type support + */ + function setSupportedIdType(bytes2 idType, bool supported) public onlyOwner { + _stateData.isIdTypeSupported[idType] = supported; + } + /** * @dev Change the state of an identity (transit to the new state) with ZKP ownership check. * @param id Identity @@ -492,23 +571,35 @@ contract State is Ownable2StepUpgradeable, IState { uint256 newState, bool isOldStateGenesis ) internal { - require(id != 0, "ID should not be zero"); - require(newState != 0, "New state should not be zero"); + if (id == 0) { + revert IdShouldNotBeZero(); + } + if (newState == 0) { + revert NewStateShouldNotBeZero(); + } if (isOldStateGenesis) { - require(!idExists(id), "Old state is genesis but identity already exists"); + if (idExists(id)) { + revert OldStateIsGenesisButIdentityAlreadyExists(); + } // Push old state to state entries, with zero timestamp and block _stateData.addGenesisState(id, oldState); } else { - require(idExists(id), "Old state is not genesis but identity does not yet exist"); + if (!idExists(id)) { + revert OldStateIsNotGenesisButIdentityDoesNotExist(); + } StateLib.EntryInfo memory prevStateInfo = _stateData.getStateInfoById(id); - require(prevStateInfo.state == oldState, "Old state does not match the latest state"); + if (prevStateInfo.state != oldState) { + revert OldStateDoesNotMatchTheLatestState(); + } } // this checks that oldState != newState as well - require(!stateExists(id, newState), "New state already exists"); + if (stateExists(id, newState)) { + revert NewStateAlreadyExists(); + } _stateData.addState(id, newState); _gistData.addLeaf(PoseidonUnit1L.poseidon([id]), newState); } @@ -574,24 +665,4 @@ contract State is Ownable2StepUpgradeable, IState { _defaultIdTypeInitialized = true; _stateData.isIdTypeSupported[defaultIdType] = true; } - - /** - * @dev Check if the id type is supported and return the id type - * @param id Identity - * trows if id type is not supported - */ - function getIdTypeIfSupported(uint256 id) public view returns (bytes2) { - bytes2 idType = GenesisUtils.getIdType(id); - require(_stateData.isIdTypeSupported[idType], "id type is not supported"); - return idType; - } - - /** - * @dev Set supported IdType setter - * @param idType id type - * @param supported ability to enable or disable id type support - */ - function setSupportedIdType(bytes2 idType, bool supported) public onlyOwner { - _stateData.isIdTypeSupported[idType] = supported; - } } diff --git a/contracts/test-helpers/AuthValidatorStub.sol b/contracts/test-helpers/AuthValidatorStub.sol new file mode 100644 index 00000000..7e89122f --- /dev/null +++ b/contracts/test-helpers/AuthValidatorStub.sol @@ -0,0 +1,38 @@ +// 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"; + +/** + * @dev AuthValidatorStub validator + */ +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; + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return + interfaceId == type(IAuthValidator).interfaceId || super.supportsInterface(interfaceId); + } + + function verify( + address, + bytes calldata, + bytes calldata + ) external view override returns (uint256, AuthResponseField[] memory) { + AuthResponseField[] memory authResponseFields = new AuthResponseField[](1); + authResponseFields[0] = AuthResponseField("challenge", 1); + return (userID, authResponseFields); + } + + // solhint-disable-next-line func-name-mixedcase + function stub_setVerifyResults(uint256 _userID) external { + userID = _userID; + } +} diff --git a/contracts/test-helpers/ERC20PermitToken.sol b/contracts/test-helpers/ERC20PermitToken.sol index 209de571..a1a8395a 100644 --- a/contracts/test-helpers/ERC20PermitToken.sol +++ b/contracts/test-helpers/ERC20PermitToken.sol @@ -1,6 +1,6 @@ pragma solidity ^0.8.27; -import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; +import {ERC20, ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; contract ERC20PermitToken is ERC20Permit { constructor(uint256 initialSupply) ERC20Permit("TEST") ERC20("EIP-2612 TEST", "EIP2612-TST") { diff --git a/contracts/test-helpers/ERC20Token.sol b/contracts/test-helpers/ERC20Token.sol index 69028fb8..fd326c6e 100644 --- a/contracts/test-helpers/ERC20Token.sol +++ b/contracts/test-helpers/ERC20Token.sol @@ -1,6 +1,6 @@ pragma solidity ^0.8.27; -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract ERC20Token is ERC20 { constructor(uint256 initialSupply) ERC20("ERC20Token", "TST") { diff --git a/contracts/test-helpers/EmbeddedVerifierWrapper.sol b/contracts/test-helpers/EmbeddedVerifierWrapper.sol new file mode 100644 index 00000000..38c2f153 --- /dev/null +++ b/contracts/test-helpers/EmbeddedVerifierWrapper.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {EmbeddedVerifier} from "../verifiers/EmbeddedVerifier.sol"; +import {IState} from "../interfaces/IState.sol"; + +contract EmbeddedVerifierWrapper is EmbeddedVerifier { + 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); + } + + function _beforeProofSubmit( + AuthResponse memory authResponse, + Response[] memory responses + ) internal override { + emit BeforeProofSubmit(authResponse, responses); + } + + function _afterProofSubmit( + AuthResponse memory authResponse, + Response[] memory responses + ) internal override { + emit AfterProofSubmit(authResponse, responses); + } +} 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..e5c39edd 100644 --- a/contracts/test-helpers/Groth16VerifierValidatorStub.sol +++ b/contracts/test-helpers/Groth16VerifierValidatorStub.sol @@ -1,15 +1,26 @@ // 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 IGroth16Verifier { + bool private verifyResult; + + constructor() { + verifyResult = true; + } -contract Groth16VerifierValidatorStub is IVerifier { function verify( uint256[2] calldata, uint256[2][2] calldata, uint256[2] calldata, uint256[] calldata - ) external pure returns (bool r) { - return true; + ) external view returns (bool r) { + return verifyResult; + } + + // solhint-disable-next-line func-name-mixedcase + function stub_setVerifyResult(bool result) external { + verifyResult = result; } } diff --git a/contracts/test-helpers/RequestDisableableTestWrapper.sol b/contracts/test-helpers/RequestDisableableTestWrapper.sol new file mode 100644 index 00000000..1fc17ec7 --- /dev/null +++ b/contracts/test-helpers/RequestDisableableTestWrapper.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.10; + +import {RequestDisableable} from "../verifiers/RequestDisableable.sol"; +import {IState} from "../interfaces/IState.sol"; +import {IVerifier} from "../interfaces/IVerifier.sol"; + +contract RequestDisableableTestWrapper is RequestDisableable { + function initialize(IState state) public initializer { + __Verifier_init(state); + } + + function disableRequest(uint256 requestId) public { + _disableRequest(requestId); + } + + function enableRequest(uint256 requestId) public { + _enableRequest(requestId); + } + + /* solhint-disable no-empty-blocks */ + function testModifier(uint256 requestId) public view onlyEnabledRequest(requestId) {} + /* solhint-enable no-empty-blocks */ + + function getRequestIfCanBeVerified( + uint256 requestId + ) public view returns (IVerifier.RequestData memory) { + return _getRequestIfCanBeVerified(requestId); + } +} diff --git a/contracts/test-helpers/RequestOwnershipTestWrapper.sol b/contracts/test-helpers/RequestOwnershipTestWrapper.sol new file mode 100644 index 00000000..36fd588b --- /dev/null +++ b/contracts/test-helpers/RequestOwnershipTestWrapper.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.10; + +import {RequestOwnership} from "../verifiers/RequestOwnership.sol"; +import {IState} from "../interfaces/IState.sol"; + +contract RequestOwnershipTestWrapper is RequestOwnership { + function initialize(IState state) public initializer { + __Verifier_init(state); + } + + function setRequestOwner(uint256 requestId, address requestOwner) public { + _setRequestOwner(requestId, requestOwner); + } +} diff --git a/contracts/test-helpers/RequestValidatorStub.sol b/contracts/test-helpers/RequestValidatorStub.sol new file mode 100644 index 00000000..6bd7ce26 --- /dev/null +++ b/contracts/test-helpers/RequestValidatorStub.sol @@ -0,0 +1,79 @@ +// 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"; + +error RequestParamNameNotFound(); + +/** + * @dev RequestValidatorStub validator + */ +contract RequestValidatorStub is IRequestValidator, ERC165 { + string public constant VERSION = "1.0.0-stub"; + + mapping(bytes32 hashParams => IRequestValidator.RequestParam[] requestParams) + private requestParams; + IRequestValidator.ResponseField[] private responseFields; + mapping(string => uint256) private _requestParamNameToIndex; + + 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( + address, + bytes calldata, + bytes calldata + ) external view returns (IRequestValidator.ResponseField[] memory) { + return responseFields; + } + + // solhint-disable-next-line func-name-mixedcase + 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 params + ) external view returns (IRequestValidator.RequestParam[] memory) { + return requestParams[keccak256(params)]; + } + + function requestParamIndexOf(string memory name) public view override returns (uint256) { + uint256 index = _requestParamNameToIndex[name]; + if (index == 0) revert RequestParamNameNotFound(); + return --index; // we save 1-based index, but return 0-based + } + + // solhint-disable-next-line func-name-mixedcase + function stub_setRequestParams( + bytes[] calldata _params, + IRequestValidator.RequestParam[][] calldata _requestParams + ) external { + for (uint256 i = 0; i < _params.length; i++) { + delete requestParams[keccak256(_params[i])]; + + for (uint256 j = 0; j < _requestParams[i].length; j++) { + requestParams[keccak256(_params[i])].push(_requestParams[i][j]); + _setRequestParamToIndex(_requestParams[i][j].name, j); + } + } + } + + function _setRequestParamToIndex(string memory requestParamName, uint256 index) internal { + // increment index to avoid 0 + _requestParamNameToIndex[requestParamName] = ++index; + } +} diff --git a/contracts/test-helpers/ReverseHashWrapper.sol b/contracts/test-helpers/ReverseHashWrapper.sol index 6e6c223f..d4f6228b 100644 --- a/contracts/test-helpers/ReverseHashWrapper.sol +++ b/contracts/test-helpers/ReverseHashWrapper.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.27; import {ReverseHashLib} from "../lib/ReverseHashLib.sol"; import {PoseidonUnit2L, PoseidonUnit3L} from "../lib/Poseidon.sol"; +error UnsupportedLength(); + contract ReverseHashWrapper { using ReverseHashLib for ReverseHashLib.Data; @@ -28,6 +30,6 @@ contract ReverseHashWrapper { if (preimage.length == 3) { return PoseidonUnit3L.poseidon([preimage[0], preimage[1], preimage[2]]); } - revert("Unsupported length"); + revert UnsupportedLength(); } } diff --git a/contracts/test-helpers/ValidatorStub.sol b/contracts/test-helpers/ValidatorStub.sol deleted file mode 100644 index 5a4136b4..00000000 --- a/contracts/test-helpers/ValidatorStub.sol +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; -import {IState} from "../interfaces/IState.sol"; - -/** - * @dev ValidatorStub validator - */ -contract ValidatorStub is ICircuitValidator, ERC165 { - string public constant VERSION = "2.0.2-mock"; - - string internal constant CIRCUIT_ID = "mock-stub"; - - string[] circuitIds = [CIRCUIT_ID]; - - function version() public pure override returns (string memory) { - return VERSION; - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return - interfaceId == type(ICircuitValidator).interfaceId || - super.supportsInterface(interfaceId); - } - - function verify( - uint256[] calldata, - uint256[2] calldata, - uint256[2][2] calldata, - uint256[2] calldata, - bytes calldata, - address - ) external pure override returns (ICircuitValidator.KeyToInputIndex[] memory) { - ICircuitValidator.KeyToInputIndex[] - memory keyToInputIndexes = new ICircuitValidator.KeyToInputIndex[](2); - keyToInputIndexes[0].key = "userID"; - keyToInputIndexes[0].inputIndex = 1; - keyToInputIndexes[1].key = "issuerID"; - keyToInputIndexes[1].inputIndex = 2; - return keyToInputIndexes; - } - - function verifyV2( - bytes calldata, - bytes calldata, - address, - IState - ) external pure override returns (ICircuitValidator.Signal[] memory) { - ICircuitValidator.Signal[] memory signals = new ICircuitValidator.Signal[](2); - signals[0].name = "userID"; - signals[0].value = 1; - signals[1].name = "issuerID"; - signals[1].value = 2; - return signals; - } - - function inputIndexOf(string memory /*name*/) external pure returns (uint256) { - return 0; - } - - function getSupportedCircuitIds() external view returns (string[] memory ids) { - return circuitIds; - } -} diff --git a/contracts/test-helpers/ValidatorWhitelistTestWrapper.sol b/contracts/test-helpers/ValidatorWhitelistTestWrapper.sol new file mode 100644 index 00000000..c32d4513 --- /dev/null +++ b/contracts/test-helpers/ValidatorWhitelistTestWrapper.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.10; + +import {ValidatorWhitelist} from "../verifiers/ValidatorWhitelist.sol"; +import {IState} from "../interfaces/IState.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IVerifier} from "../interfaces/IVerifier.sol"; + +contract ValidatorWhitelistTestWrapper is ValidatorWhitelist { + function initialize(IState state) public initializer { + __Verifier_init(state); + } + + function addValidatorToWhitelist(IRequestValidator validator) public { + _addValidatorToWhitelist(validator); + } + + function removeValidatorFromWhitelist(IRequestValidator validator) public { + _removeValidatorFromWhitelist(validator); + } + + /* solhint-disable no-empty-blocks */ + function testModifier( + IRequestValidator validator + ) public view onlyWhitelistedValidator(validator) {} + /* solhint-enable no-empty-blocks */ + + function getRequestIfCanBeVerified( + uint256 requestId + ) public view returns (IVerifier.RequestData memory) { + return _getRequestIfCanBeVerified(requestId); + } +} diff --git a/contracts/test-helpers/VerifierTestWrapper.sol b/contracts/test-helpers/VerifierTestWrapper.sol new file mode 100644 index 00000000..7509db25 --- /dev/null +++ b/contracts/test-helpers/VerifierTestWrapper.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0 +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); + } + + function setVerifierID(uint256 verifierID) public { + _setVerifierID(verifierID); + } +} diff --git a/contracts/validators/AuthV2Validator.sol b/contracts/validators/AuthV2Validator.sol deleted file mode 100644 index 46ecdf0b..00000000 --- a/contracts/validators/AuthV2Validator.sol +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {CredentialAtomicQueryValidatorBase} from "./CredentialAtomicQueryValidatorBase.sol"; -import {IVerifier} from "../interfaces/IVerifier.sol"; -import {GenesisUtils} from "../lib/GenesisUtils.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; -import {IState} from "../interfaces/IState.sol"; - -/** - * @dev AuthV2Validator validator - */ -contract AuthV2Validator is CredentialAtomicQueryValidatorBase { - 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 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); - _setInputToIndex("challenge", 1); - _setInputToIndex("gistRoot", 2); - - _initDefaultStateVariables(_stateContractAddr, _verifierContractAddr, CIRCUIT_ID, 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 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. - */ - 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"); - } - - /** - * @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, - // solhint-disable-next-line no-unused-vars - bytes calldata data, - address sender, - IState stateContract - ) public view override returns (ICircuitValidator.Signal[] 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])); - - PubSignals memory pubSignals = parsePubSignals(inputs); - _checkGistRoot(pubSignals.userID, pubSignals.gistRoot, stateContract); - _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; - } - - function _verifyZKP( - uint256[] memory inputs, - uint256[2] memory a, - 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"); - - // 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 deleted file mode 100644 index a307062b..00000000 --- a/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {CredentialAtomicQueryValidatorBase} from "./CredentialAtomicQueryValidatorBase.sol"; -import {IVerifier} from "../interfaces/IVerifier.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; -import {IState} from "../interfaces/IState.sol"; - -/** - * @dev Base contract for credential atomic query v2 validators circuits. - */ -abstract contract CredentialAtomicQueryV2ValidatorBase is CredentialAtomicQueryValidatorBase { - /** - * @dev Version of contract - */ - - struct CredentialAtomicQuery { - uint256 schema; - uint256 claimPathKey; - uint256 operator; - uint256 slotIndex; - uint256[] value; - uint256 queryHash; - uint256[] allowedIssuers; - string[] circuitIds; - bool skipClaimRevocationCheck; - // 0 for inclusion in merklized credentials, 1 for non-inclusion and for non-merklized credentials - uint256 claimPathNotExists; - } - - struct PubSignals { - uint256 merklized; - uint256 userID; - uint256 issuerState; - uint256 circuitQueryHash; - uint256 requestID; - uint256 challenge; - uint256 gistRoot; - uint256 issuerID; - uint256 isRevocationChecked; - uint256 issuerClaimNonRevState; - uint256 timestamp; - } - - /** - * @dev Get the version of the contract - * @return Version of the contract - */ - function version() public pure virtual override returns (string memory); - - /** - * @dev Parse the public signals - * @param inputs Array of public inputs - * @return Parsed public signals - */ - function parsePubSignals( - uint256[] memory inputs - ) public pure virtual returns (PubSignals memory); - - /** - * @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. - */ - 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. - * @return Array of public signals as result. - */ - function verifyV2( - bytes calldata zkProof, - bytes calldata data, - address sender, - IState stateContract - ) public view override returns (ICircuitValidator.Signal[] 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])); - - PubSignals memory pubSignals = _verifyMain(inputs, a, b, c, data, sender, stateContract); - return _getSpecialSignals(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. - * @param state State contract to get identities and gist states to check. - */ - function _verifyMain( - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - bytes calldata data, - address sender, - IState state - ) internal view returns (PubSignals memory) { - CredentialAtomicQuery memory credAtomicQuery = abi.decode(data, (CredentialAtomicQuery)); - - 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"); - - // verify that zkp is valid - require(verifier.verify(a, b, c, inputs), "Proof is not valid"); - - PubSignals memory pubSignals = parsePubSignals(inputs); - - // check circuitQueryHash - require( - pubSignals.circuitQueryHash == credAtomicQuery.queryHash, - "Query hash does not match the requested one" - ); - - // TODO: add support for query to specific userID and then verifying it - - _checkMerklized(pubSignals.merklized, credAtomicQuery.claimPathKey); - _checkAllowedIssuers(pubSignals.issuerID, credAtomicQuery.allowedIssuers); - _checkProofExpiration(pubSignals.timestamp); - _checkIsRevocationChecked( - pubSignals.isRevocationChecked, - credAtomicQuery.skipClaimRevocationCheck - ); - - // Checking challenge to prevent replay attacks from other addresses - _checkChallenge(pubSignals.challenge, sender); - - // GIST root and state checks - _checkGistRoot(pubSignals.userID, pubSignals.gistRoot, state); - _checkClaimIssuanceState(pubSignals.issuerID, pubSignals.issuerState, state); - _checkClaimNonRevState(pubSignals.issuerID, pubSignals.issuerClaimNonRevState, state); - - return pubSignals; - } - - function _checkMerklized(uint256 merklized, uint256 queryClaimPathKey) internal pure { - uint256 shouldBeMerklized = queryClaimPathKey != 0 ? 1 : 0; - require(merklized == shouldBeMerklized, "Merklized value is not correct"); - } - - function _checkIsRevocationChecked( - uint256 isRevocationChecked, - bool skipClaimRevocationCheck - ) internal pure { - uint256 expectedIsRevocationChecked = 1; - if (skipClaimRevocationCheck) { - expectedIsRevocationChecked = 0; - } - require( - isRevocationChecked == expectedIsRevocationChecked, - "Revocation check should match the query" - ); - } - - function _getSpecialSignals( - 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") - }); - keyToInputIndexes[1] = ICircuitValidator.KeyToInputIndex({ - key: "timestamp", - inputIndex: inputIndexOf("timestamp") - }); - keyToInputIndexes[2] = ICircuitValidator.KeyToInputIndex({ - key: "issuerID", - inputIndex: inputIndexOf("issuerID") - }); - return keyToInputIndexes; - } -} diff --git a/contracts/validators/auth/AuthV2Validator.sol b/contracts/validators/auth/AuthV2Validator.sol new file mode 100644 index 00000000..25ea1d87 --- /dev/null +++ b/contracts/validators/auth/AuthV2Validator.sol @@ -0,0 +1,241 @@ +// 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(); + +/** + * @dev AuthV2 validator for auth + */ +contract AuthV2Validator 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)); + // solhint-disable-next-line const-name-snakecase + bytes32 private constant AuthV2ValidatorStorageLocation = + 0x5212d71c1540b1d75013e45246a2b44f2ee9363a102ea02fac1792932b691600; + + /// @dev Get the main storage using assembly to ensure specific storage location + function _getAuthV2ValidatorStorage() private pure returns (AuthV2ValidatorStorage storage $) { + // solhint-disable-next-line no-inline-assembly + assembly { + $.slot := AuthV2ValidatorStorageLocation + } + } + + /** + * @dev Initialize the contract + * @param _stateContractAddr Address of the state contract + * @param _verifierContractAddr Address of the verifier contract + * @param owner Owner of the contract + */ + function initialize( + address _stateContractAddr, + address _verifierContractAddr, + address owner + ) public initializer { + _initDefaultStateVariables(_stateContractAddr, _verifierContractAddr, CIRCUIT_ID, 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 sender Sender of the proof. + * @param proof Proof packed as bytes to verify. + * @param params Request query data of the credential to verify. + * @return userID user ID of public signals as result. + */ + function verify( + // solhint-disable-next-line no-unused-vars + address sender, + bytes calldata proof, + // solhint-disable-next-line no-unused-vars + bytes calldata params + ) public view override returns (uint256 userID, AuthResponseField[] memory) { + ( + 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); + _verifyZKP(inputs, a, b, c); + + AuthResponseField[] memory authResponseFields = new AuthResponseField[](1); + authResponseFields[0] = AuthResponseField("challenge", pubSignals.challenge); + return (pubSignals.userID, authResponseFields); + } + + /** + * @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]; + } + + /** + * @dev Set the expiration timeout for the revocation state + * @param expirationTimeout The expiration timeout for the revocation state + */ + function setRevocationStateExpirationTimeout( + uint256 expirationTimeout + ) public virtual onlyOwner { + _getAuthV2ValidatorStorage().revocationStateExpirationTimeout = expirationTimeout; + } + + /** + * @dev Get the expiration timeout for the revocation state + * @return The expiration timeout for the revocation state + */ + function getRevocationStateExpirationTimeout() public view virtual returns (uint256) { + return _getAuthV2ValidatorStorage().revocationStateExpirationTimeout; + } + + /** + * @dev Set the expiration timeout for the proof + * @param expirationTimeout The expiration timeout for the proof + */ + function setProofExpirationTimeout(uint256 expirationTimeout) public virtual onlyOwner { + _getAuthV2ValidatorStorage().proofExpirationTimeout = expirationTimeout; + } + + /** + * @dev Get the expiration timeout for the proof + * @return The expiration timeout for the proof + */ + function getProofExpirationTimeout() public view virtual returns (uint256) { + return _getAuthV2ValidatorStorage().proofExpirationTimeout; + } + + /** + * @dev Set the expiration timeout for the gist root + * @param expirationTimeout The expiration timeout for the gist root + */ + function setGISTRootExpirationTimeout(uint256 expirationTimeout) public virtual onlyOwner { + _getAuthV2ValidatorStorage().gistRootExpirationTimeout = expirationTimeout; + } + + /** + * @dev Get the expiration timeout for the gist root + * @return The expiration timeout for the gist root + */ + function getGISTRootExpirationTimeout() public view virtual returns (uint256) { + return _getAuthV2ValidatorStorage().gistRootExpirationTimeout; + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return + interfaceId == type(IAuthValidator).interfaceId || super.supportsInterface(interfaceId); + } + + 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); + } + + function _getState() internal view returns (IState) { + return _getAuthV2ValidatorStorage().state; + } + + function _checkGistRoot(uint256 _id, uint256 _gistRoot) internal view { + AuthV2ValidatorStorage storage $ = _getAuthV2ValidatorStorage(); + bytes2 idType = GenesisUtils.getIdType(_id); + uint256 replacedAt = _getState().getGistRootReplacedAt(idType, _gistRoot); + + if (replacedAt != 0 && block.timestamp > $.gistRootExpirationTimeout + replacedAt) { + revert GistRootIsExpired(); + } + } + + function _verifyZKP( + uint256[] memory inputs, + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c + ) internal view { + IGroth16Verifier g16Verifier = getVerifierByCircuitId(CIRCUIT_ID); + if (g16Verifier == IGroth16Verifier(address(0))) { + revert VerifierAddressShouldNotBeZero(); + } + + // verify that zkp is valid + if (!g16Verifier.verify(a, b, c, inputs)) { + revert ProofIsNotValid(); + } + } +} diff --git a/contracts/validators/auth/EthIdentityValidator.sol b/contracts/validators/auth/EthIdentityValidator.sol new file mode 100644 index 00000000..4f513155 --- /dev/null +++ b/contracts/validators/auth/EthIdentityValidator.sol @@ -0,0 +1,103 @@ +// 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 {GenesisUtils} from "../../lib/GenesisUtils.sol"; +import {IAuthValidator} from "../../interfaces/IAuthValidator.sol"; +import {IState} from "../../interfaces/IState.sol"; + +error SenderIsNotIdentityOwner(); + +/** + * @dev EthIdentityValidator validator + */ +contract EthIdentityValidator is Ownable2StepUpgradeable, IAuthValidator, 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)); + // solhint-disable-next-line const-name-snakecase + bytes32 private constant EthIdentityValidatorBaseStorageLocation = + 0x1816cff28d525c2e505742319020369d0e29e8fafd5168e127e29766cf2be1fb; + + /// @dev Get the main storage using assembly to ensure specific storage location + function _getEthIdentityValidatorBaseStorage() + private + pure + returns (EthIdentityValidatorBaseStorage storage $) + { + // solhint-disable-next-line no-inline-assembly + assembly { + $.slot := EthIdentityValidatorBaseStorageLocation + } + } + + /** + * @dev Initialize the contract + * @param _stateContractAddr Address of the state contract + * @param owner Owner of the contract + */ + function initialize(address _stateContractAddr, address owner) public initializer { + _initDefaultStateVariables(_stateContractAddr, 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 sender Sender of the proof. + * @param proof Proof packed as bytes to verify. + * @param params Request query data of the credential to verify. + * @return Array of signals as result. + */ + function verify( + address sender, + bytes calldata proof, + // solhint-disable-next-line no-unused-vars + bytes calldata params + ) public view override returns (uint256, AuthResponseField[] memory) { + uint256 userID = abi.decode(proof, (uint256)); + + _verifyEthIdentity(userID, sender); + return (userID, new AuthResponseField[](0)); + } + + function _getState() internal view returns (IState) { + return _getEthIdentityValidatorBaseStorage().state; + } + + function _initDefaultStateVariables(address _stateContractAddr, address owner) internal { + EthIdentityValidatorBaseStorage storage s = _getEthIdentityValidatorBaseStorage(); + + s.state = IState(_stateContractAddr); + __Ownable_init(owner); + } + + function _verifyEthIdentity(uint256 id, address sender) internal view { + bytes2 idType = _getState().getIdTypeIfSupported(id); + uint256 calcId = GenesisUtils.calcIdFromEthAddress(idType, sender); + if (calcId != id) { + revert SenderIsNotIdentityOwner(); + } + } +} diff --git a/contracts/validators/CredentialAtomicQueryMTPV2Validator.sol b/contracts/validators/request/CredentialAtomicQueryMTPV2Validator.sol similarity index 94% rename from contracts/validators/CredentialAtomicQueryMTPV2Validator.sol rename to contracts/validators/request/CredentialAtomicQueryMTPV2Validator.sol index 03dd5334..726dd57c 100644 --- a/contracts/validators/CredentialAtomicQueryMTPV2Validator.sol +++ b/contracts/validators/request/CredentialAtomicQueryMTPV2Validator.sol @@ -16,13 +16,13 @@ contract CredentialAtomicQueryMTPV2Validator is CredentialAtomicQueryV2Validator /** * @dev Initialize the contract - * @param _verifierContractAddr Address of the verifier contract * @param _stateContractAddr Address of the state contract + * @param _verifierContractAddr Address of the verifier contract * @param owner Owner of the contract */ function initialize( - address _verifierContractAddr, address _stateContractAddr, + address _verifierContractAddr, address owner ) public initializer { _setInputToIndex("merklized", 0); @@ -37,6 +37,10 @@ contract CredentialAtomicQueryMTPV2Validator is CredentialAtomicQueryV2Validator _setInputToIndex("issuerClaimNonRevState", 9); _setInputToIndex("timestamp", 10); + _setRequestParamToIndex("groupID", 0); + _setRequestParamToIndex("verifierID", 1); + _setRequestParamToIndex("nullifierSessionID", 2); + _initDefaultStateVariables(_stateContractAddr, _verifierContractAddr, CIRCUIT_ID, owner); } diff --git a/contracts/validators/CredentialAtomicQuerySigV2Validator.sol b/contracts/validators/request/CredentialAtomicQuerySigV2Validator.sol similarity index 86% rename from contracts/validators/CredentialAtomicQuerySigV2Validator.sol rename to contracts/validators/request/CredentialAtomicQuerySigV2Validator.sol index 97f69ba4..7c600789 100644 --- a/contracts/validators/CredentialAtomicQuerySigV2Validator.sol +++ b/contracts/validators/request/CredentialAtomicQuerySigV2Validator.sol @@ -16,13 +16,13 @@ contract CredentialAtomicQuerySigV2Validator is CredentialAtomicQueryV2Validator /** * @dev Initialize the contract + * @param _stateContractAddress Address of the state contract * @param _verifierContractAddr Address of the verifier contract - * @param _stateContractAddr Address of the state contract * @param owner Owner of the contract */ function initialize( + address _stateContractAddress, address _verifierContractAddr, - address _stateContractAddr, address owner ) public initializer { _setInputToIndex("merklized", 0); @@ -37,7 +37,11 @@ contract CredentialAtomicQuerySigV2Validator is CredentialAtomicQueryV2Validator _setInputToIndex("issuerClaimNonRevState", 9); _setInputToIndex("timestamp", 10); - _initDefaultStateVariables(_stateContractAddr, _verifierContractAddr, CIRCUIT_ID, owner); + _setRequestParamToIndex("groupID", 0); + _setRequestParamToIndex("verifierID", 1); + _setRequestParamToIndex("nullifierSessionID", 2); + + _initDefaultStateVariables(_stateContractAddress, _verifierContractAddr, CIRCUIT_ID, owner); } /** diff --git a/contracts/validators/request/CredentialAtomicQueryV2ValidatorBase.sol b/contracts/validators/request/CredentialAtomicQueryV2ValidatorBase.sol new file mode 100644 index 00000000..8316e557 --- /dev/null +++ b/contracts/validators/request/CredentialAtomicQueryV2ValidatorBase.sol @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {CredentialAtomicQueryValidatorBase} from "./CredentialAtomicQueryValidatorBase.sol"; +import {IGroth16Verifier} from "../../interfaces/IGroth16Verifier.sol"; +import {IRequestValidator} from "../../interfaces/IRequestValidator.sol"; + +error CircuitsLengthShouldBeOne(); +error VerifierAddressShouldNotBeZero(); +error ProofIsNotValid(); +error QueryHashDoesNotMatchTheRequestedOne(uint256 expected, uint256 actual); +error MerklizedValueIsNotCorrect(); +error RevocationCheckShouldMatchTheQuery(uint256 expected, uint256 actual); + +/** + * @dev Base contract for credential atomic query v2 validators circuits. + */ +abstract contract CredentialAtomicQueryV2ValidatorBase is CredentialAtomicQueryValidatorBase { + /** + * @dev Version of contract + */ + + struct CredentialAtomicQuery { + uint256 schema; + uint256 claimPathKey; + uint256 operator; + uint256 slotIndex; + uint256[] value; + uint256 queryHash; + uint256[] allowedIssuers; + string[] circuitIds; + bool skipClaimRevocationCheck; + // 0 for inclusion in merklized credentials, 1 for non-inclusion and for non-merklized credentials + uint256 claimPathNotExists; + } + + struct PubSignals { + uint256 merklized; + uint256 userID; + uint256 issuerState; + uint256 circuitQueryHash; + uint256 requestID; + uint256 challenge; + uint256 gistRoot; + uint256 issuerID; + uint256 isRevocationChecked; + uint256 issuerClaimNonRevState; + uint256 timestamp; + } + + /** + * @dev Get the version of the contract + * @return Version of the contract + */ + function version() public pure virtual override returns (string memory); + + /** + * @dev Parse the public signals + * @param inputs Array of public inputs + * @return Parsed public signals + */ + function parsePubSignals( + uint256[] memory inputs + ) public pure virtual returns (PubSignals memory); + + /** + * @dev Verify the groth16 proof and check the request query data + * @param sender Sender of the proof. + * @param proof Proof packed as bytes to verify. + * @param params Request query data of the credential to verify. + * @return Array of public signals as result. + */ + function verify( + address sender, + bytes calldata proof, + bytes calldata params + ) public view override returns (IRequestValidator.ResponseField[] memory) { + PubSignals memory pubSignals = _verifyMain(sender, proof, params); + return _getResponseFields(pubSignals); + } + + /** + * @dev Get the request params for V2 circuit validator. + * @return RequestParams for V2 circuit validator. + */ + function getRequestParams( + bytes calldata + ) external pure override returns (IRequestValidator.RequestParam[] memory) { + IRequestValidator.RequestParam[] + memory requestParams = new IRequestValidator.RequestParam[](3); + requestParams[0] = IRequestValidator.RequestParam({name: "groupID", value: 0}); + requestParams[1] = IRequestValidator.RequestParam({name: "verifierID", value: 0}); + requestParams[2] = IRequestValidator.RequestParam({name: "nullifierSessionID", value: 0}); + return requestParams; + } + + /** + * @dev Verify the groth16 proof and check the request query data + * @param sender Sender of the proof. + * @param proof the groth16 proof. + * @param params Request query data of the credential to verify. + */ + function _verifyMain( + address sender, + bytes calldata proof, + bytes calldata params + ) internal view returns (PubSignals memory) { + ( + 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])); + + CredentialAtomicQuery memory credAtomicQuery = abi.decode(params, (CredentialAtomicQuery)); + + if (credAtomicQuery.circuitIds.length != 1) { + revert CircuitsLengthShouldBeOne(); + } + + IGroth16Verifier g16Verifier = getVerifierByCircuitId(credAtomicQuery.circuitIds[0]); + + if (g16Verifier == IGroth16Verifier(address(0))) { + revert VerifierAddressShouldNotBeZero(); + } + + // verify that zkp is valid + if (!g16Verifier.verify(a, b, c, inputs)) { + revert ProofIsNotValid(); + } + + PubSignals memory pubSignals = parsePubSignals(inputs); + + // check circuitQueryHash + if (pubSignals.circuitQueryHash != credAtomicQuery.queryHash) { + revert QueryHashDoesNotMatchTheRequestedOne( + credAtomicQuery.queryHash, + pubSignals.circuitQueryHash + ); + } + + // TODO: add support for query to specific userID and then verifying it + + _checkMerklized(pubSignals.merklized, credAtomicQuery.claimPathKey); + _checkAllowedIssuers(pubSignals.issuerID, credAtomicQuery.allowedIssuers); + _checkProofExpiration(pubSignals.timestamp); + _checkIsRevocationChecked( + pubSignals.isRevocationChecked, + credAtomicQuery.skipClaimRevocationCheck + ); + + // Checking challenge to prevent replay attacks from other addresses + _checkChallenge(pubSignals.challenge, sender); + + // GIST root and state checks + _checkGistRoot(pubSignals.userID, pubSignals.gistRoot); + _checkClaimIssuanceState(pubSignals.issuerID, pubSignals.issuerState); + _checkClaimNonRevState(pubSignals.issuerID, pubSignals.issuerClaimNonRevState); + + return pubSignals; + } + + function _checkMerklized(uint256 merklized, uint256 queryClaimPathKey) internal pure { + uint256 shouldBeMerklized = queryClaimPathKey != 0 ? 1 : 0; + if (merklized != shouldBeMerklized) { + revert MerklizedValueIsNotCorrect(); + } + } + + function _checkIsRevocationChecked( + uint256 isRevocationChecked, + bool skipClaimRevocationCheck + ) internal pure { + uint256 expectedIsRevocationChecked = 1; + if (skipClaimRevocationCheck) { + expectedIsRevocationChecked = 0; + } + if (isRevocationChecked != expectedIsRevocationChecked) { + revert RevocationCheckShouldMatchTheQuery( + expectedIsRevocationChecked, + isRevocationChecked + ); + } + } + + function _getResponseFields( + PubSignals memory pubSignals + ) internal pure returns (IRequestValidator.ResponseField[] memory) { + IRequestValidator.ResponseField[] + memory responseFields = new IRequestValidator.ResponseField[](3); + responseFields[0] = IRequestValidator.ResponseField({ + name: "userID", + value: pubSignals.userID + }); + responseFields[1] = IRequestValidator.ResponseField({ + name: "timestamp", + value: pubSignals.timestamp + }); + responseFields[2] = IRequestValidator.ResponseField({ + name: "issuerID", + value: pubSignals.issuerID + }); + return responseFields; + } +} diff --git a/contracts/validators/CredentialAtomicQueryV3Validator.sol b/contracts/validators/request/CredentialAtomicQueryV3Validator.sol similarity index 53% rename from contracts/validators/CredentialAtomicQueryV3Validator.sol rename to contracts/validators/request/CredentialAtomicQueryV3Validator.sol index c704ee2b..3bb9f901 100644 --- a/contracts/validators/CredentialAtomicQueryV3Validator.sol +++ b/contracts/validators/request/CredentialAtomicQueryV3Validator.sol @@ -2,10 +2,19 @@ pragma solidity 0.8.27; import {CredentialAtomicQueryValidatorBase} from "./CredentialAtomicQueryValidatorBase.sol"; -import {IVerifier} from "../interfaces/IVerifier.sol"; -import {GenesisUtils} from "../lib/GenesisUtils.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; -import {IState} from "../interfaces/IState.sol"; +import {IGroth16Verifier} from "../../interfaces/IGroth16Verifier.sol"; +import {GenesisUtils} from "../../lib/GenesisUtils.sol"; +import {IRequestValidator} from "../../interfaces/IRequestValidator.sol"; + +error VerifierIDNotSet(); +error QueryHashDoesNotMatchTheRequestedOne(uint256 expected, uint256 actual); +error VerifierAddressShouldNotBeZero(); +error CircuitsLengthShouldBeOne(); +error ProofIsNotValid(); +error InvalidLinkIDPubSignal(); +error ProofTypeShouldMatchTheRequestedOneInQuery(); +error InvalidNullifyPubSignal(); +error UserIDDoesNotCorrespondToTheSender(); /** * @dev CredentialAtomicQueryV3 validator @@ -53,13 +62,13 @@ contract CredentialAtomicQueryV3Validator is CredentialAtomicQueryValidatorBase /** * @dev Initialize the contract - * @param _verifierContractAddr Address of the verifier contract * @param _stateContractAddr Address of the state contract + * @param _verifierContractAddr Address of the verifier contract * @param owner Owner of the contract */ function initialize( - address _verifierContractAddr, address _stateContractAddr, + address _verifierContractAddr, address owner ) public initializer { _setInputToIndex("userID", 0); @@ -77,6 +86,10 @@ contract CredentialAtomicQueryV3Validator is CredentialAtomicQueryValidatorBase _setInputToIndex("timestamp", 12); _setInputToIndex("isBJJAuthEnabled", 13); + _setRequestParamToIndex("groupID", 0); + _setRequestParamToIndex("verifierID", 1); + _setRequestParamToIndex("nullifierSessionID", 2); + _initDefaultStateVariables(_stateContractAddr, _verifierContractAddr, CIRCUIT_ID, owner); } @@ -116,83 +129,72 @@ 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 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 proof Proof packed as bytes to verify. + * @param params Request query data of the credential to verify. + * @return Array of public signals 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); + address sender, + bytes calldata proof, + bytes calldata params + ) public view override returns (IRequestValidator.ResponseField[] memory) { + (PubSignals memory pubSignals, bool hasSD) = _verifyMain(sender, proof, params); + return _getResponseFields(pubSignals, 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. - * @return Array of public signals as result. + * @dev Get the request params of the request query data. + * @param params Request query data of the credential to verify. + * @return RequestParams of the request query data. */ - function verifyV2( - bytes calldata zkProof, - bytes calldata data, - address sender, - IState stateContract - ) public view override returns (ICircuitValidator.Signal[] 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])); - - (PubSignals memory pubSignals, bool hasSD) = _verifyMain( - inputs, - a, - b, - c, - data, - sender, - stateContract + function getRequestParams( + bytes calldata params + ) external pure override returns (IRequestValidator.RequestParam[] memory) { + CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( + params, + (CredentialAtomicQueryV3) ); - return _getSpecialSignals(pubSignals, hasSD); + + if (credAtomicQuery.verifierID == 0) revert VerifierIDNotSet(); + IRequestValidator.RequestParam[] + memory requestParams = new IRequestValidator.RequestParam[](3); + requestParams[0] = IRequestValidator.RequestParam({ + name: "groupID", + value: credAtomicQuery.groupID + }); + requestParams[1] = IRequestValidator.RequestParam({ + name: "verifierID", + value: credAtomicQuery.verifierID + }); + requestParams[2] = IRequestValidator.RequestParam({ + name: "nullifierSessionID", + value: credAtomicQuery.nullifierSessionID + }); + return requestParams; } /** * @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. - * @param state State contract to get identities and gist states to check. + * @param proof the groth16 proof. + * @param params Request query data of the credential to verify. */ function _verifyMain( - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - bytes calldata data, address sender, - IState state + bytes calldata proof, + bytes calldata params ) internal view returns (PubSignals memory, bool) { CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( - data, + params, (CredentialAtomicQueryV3) ); + ( + 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])); _verifyZKP(inputs, a, b, c, credAtomicQuery); @@ -206,10 +208,10 @@ contract CredentialAtomicQueryV3Validator is CredentialAtomicQueryValidatorBase _checkNullify(pubSignals.nullifier, credAtomicQuery.nullifierSessionID); // GIST root and state checks - _checkClaimIssuanceState(pubSignals.issuerID, pubSignals.issuerState, state); - _checkClaimNonRevState(pubSignals.issuerID, pubSignals.issuerClaimNonRevState, state); + _checkClaimIssuanceState(pubSignals.issuerID, pubSignals.issuerState); + _checkClaimNonRevState(pubSignals.issuerID, pubSignals.issuerClaimNonRevState); if (pubSignals.isBJJAuthEnabled == 1) { - _checkGistRoot(pubSignals.userID, pubSignals.gistRoot, state); + _checkGistRoot(pubSignals.userID, pubSignals.gistRoot); } else { _checkAuth(pubSignals.userID, sender); } @@ -218,10 +220,12 @@ contract CredentialAtomicQueryV3Validator is CredentialAtomicQueryValidatorBase _checkChallenge(pubSignals.challenge, sender); // check circuitQueryHash - require( - pubSignals.circuitQueryHash == credAtomicQuery.queryHash, - "Query hash does not match the requested one" - ); + if (pubSignals.circuitQueryHash != credAtomicQuery.queryHash) { + revert QueryHashDoesNotMatchTheRequestedOne( + credAtomicQuery.queryHash, + pubSignals.circuitQueryHash + ); + } // if operator == 16 then we have selective disclosure return (pubSignals, credAtomicQuery.operator == 16); @@ -234,102 +238,86 @@ contract CredentialAtomicQueryV3Validator is CredentialAtomicQueryValidatorBase uint256[2] memory c, CredentialAtomicQueryV3 memory credAtomicQuery ) internal view { - require(credAtomicQuery.circuitIds.length == 1, "circuitIds length is not equal to 1"); + if (credAtomicQuery.circuitIds.length != 1) { + revert CircuitsLengthShouldBeOne(); + } - IVerifier verifier = getVerifierByCircuitId(credAtomicQuery.circuitIds[0]); - require(verifier != IVerifier(address(0)), "Verifier address should not be zero"); + IGroth16Verifier g16Verifier = getVerifierByCircuitId(credAtomicQuery.circuitIds[0]); + if (g16Verifier == IGroth16Verifier(address(0))) { + revert VerifierAddressShouldNotBeZero(); + } // verify that zkp is valid - require(verifier.verify(a, b, c, inputs), "Proof is not valid"); + if (!g16Verifier.verify(a, b, c, inputs)) { + revert ProofIsNotValid(); + } } function _checkLinkID(uint256 groupID, uint256 linkID) internal pure { - require( - (groupID == 0 && linkID == 0) || (groupID != 0 && linkID != 0), - "Invalid Link ID pub signal" - ); + if (!((groupID == 0 && linkID == 0) || (groupID != 0 && linkID != 0))) { + revert InvalidLinkIDPubSignal(); + } } function _checkProofType(uint256 queryProofType, uint256 pubSignalProofType) internal pure { - require( - queryProofType == 0 || queryProofType == pubSignalProofType, - "Proof type should match the requested one in query" - ); + if (queryProofType != 0 && queryProofType != pubSignalProofType) { + revert ProofTypeShouldMatchTheRequestedOneInQuery(); + } } function _checkNullify(uint256 nullifier, uint256 nullifierSessionID) internal pure { - require(nullifierSessionID == 0 || nullifier != 0, "Invalid nullify pub signal"); + if (nullifierSessionID != 0 && nullifier == 0) { + revert InvalidNullifyPubSignal(); + } } function _checkAuth(uint256 userID, address ethIdentityOwner) internal view { - require( - userID == - GenesisUtils.calcIdFromEthAddress( - _getState().getIdTypeIfSupported(userID), - ethIdentityOwner - ), - "UserID does not correspond to the sender" - ); - } - - function _getSpecialSignals( - 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 - }); + if ( + userID != + GenesisUtils.calcIdFromEthAddress( + _getState().getIdTypeIfSupported(userID), + ethIdentityOwner + ) + ) { + revert UserIDDoesNotCorrespondToTheSender(); } - signals[i++] = ICircuitValidator.Signal({name: "timestamp", value: pubSignals.timestamp}); - signals[i++] = ICircuitValidator.Signal({name: "issuerID", value: pubSignals.issuerID}); - - return signals; } - function _getSpecialInputIndexes( + function _getResponseFields( + PubSignals memory pubSignals, 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") + uint256 i = 0; + responseFields[i++] = IRequestValidator.ResponseField({ + name: "userID", + value: pubSignals.userID + }); + responseFields[i++] = IRequestValidator.ResponseField({ + name: "linkID", + value: pubSignals.linkID }); - keyToInputIndexes[i++] = ICircuitValidator.KeyToInputIndex({ - key: "linkID", - inputIndex: inputIndexOf("linkID") + responseFields[i++] = IRequestValidator.ResponseField({ + name: "nullifier", + value: pubSignals.nullifier }); - keyToInputIndexes[i++] = ICircuitValidator.KeyToInputIndex({ - key: "nullifier", - inputIndex: inputIndexOf("nullifier") + responseFields[i++] = IRequestValidator.ResponseField({ + name: "timestamp", + value: pubSignals.timestamp + }); + responseFields[i++] = IRequestValidator.ResponseField({ + name: "issuerID", + value: pubSignals.issuerID }); 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") - }); - keyToInputIndexes[i++] = ICircuitValidator.KeyToInputIndex({ - key: "issuerID", - inputIndex: inputIndexOf("issuerID") - }); - - return keyToInputIndexes; + return responseFields; } } diff --git a/contracts/validators/CredentialAtomicQueryValidatorBase.sol b/contracts/validators/request/CredentialAtomicQueryValidatorBase.sol similarity index 71% rename from contracts/validators/CredentialAtomicQueryValidatorBase.sol rename to contracts/validators/request/CredentialAtomicQueryValidatorBase.sol index a972a12e..1255b437 100644 --- a/contracts/validators/CredentialAtomicQueryValidatorBase.sol +++ b/contracts/validators/request/CredentialAtomicQueryValidatorBase.sol @@ -3,34 +3,45 @@ 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 {IVerifier} from "../interfaces/IVerifier.sol"; -import {IState} from "../interfaces/IState.sol"; -import {PrimitiveTypeUtils} from "../lib/PrimitiveTypeUtils.sol"; +import {GenesisUtils} from "../../lib/GenesisUtils.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"; + +error InputNameNotFound(); +error RequestParamNameNotFound(); +error ChallengeShouldMatchTheSender(); +error GistRootIsExpired(); +error NonRevocationStateOfIssuerIsExpired(); +error ProofGeneratedInTheFutureIsNotValid(); +error GeneratedProofIsOutdated(); +error IssuerIsNotOnTheAllowedIssuersList(); /** * @dev Base contract for credential atomic query validators circuits. */ abstract contract CredentialAtomicQueryValidatorBase is Ownable2StepUpgradeable, - ICircuitValidator, + IRequestValidator, ERC165 { /// @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; uint256 proofExpirationTimeout; uint256 gistRootExpirationTimeout; mapping(string => uint256) _inputNameToIndex; + mapping(string => uint256) _requestParamNameToIndex; } // keccak256(abi.encode(uint256(keccak256("iden3.storage.CredentialAtomicQueryValidator")) - 1)) // & ~bytes32(uint256(0xff)); + // solhint-disable-next-line const-name-snakecase bytes32 private constant CredentialAtomicQueryValidatorBaseStorageLocation = 0x28c92975a30f1f2f7970a65953987652034d896ba2d3b7a4961ada9e18287500; @@ -40,29 +51,12 @@ abstract contract CredentialAtomicQueryValidatorBase is pure returns (CredentialAtomicQueryValidatorBaseStorage storage $) { + // solhint-disable-next-line no-inline-assembly assembly { $.slot := CredentialAtomicQueryValidatorBaseStorageLocation } } - function _initDefaultStateVariables( - address _stateContractAddr, - address _verifierContractAddr, - string memory circuitId, - address owner - ) internal { - CredentialAtomicQueryValidatorBaseStorage - storage s = _getCredentialAtomicQueryValidatorBaseStorage(); - - s.revocationStateExpirationTimeout = 1 hours; - s.proofExpirationTimeout = 1 hours; - s.gistRootExpirationTimeout = 1 hours; - s._supportedCircuitIds = [circuitId]; - s._circuitIdToVerifier[circuitId] = IVerifier(_verifierContractAddr); - s.state = IState(_stateContractAddr); - __Ownable_init(owner); - } - /** * @dev Returns the version of the contract * @return The version of the contract @@ -138,38 +132,18 @@ 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. - * @param data Request query data of the credential to verify. + * @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 sender Sender of the proof. - * @return Array of key to public input index as result. + * @param proof Proof packed as bytes to verify. + * @param params Request query data of the credential to verify. + * @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 data, address sender, - IState stateContract - ) external view virtual returns (ICircuitValidator.Signal[] memory); + bytes calldata proof, + bytes calldata params + ) external view virtual returns (ResponseField[] memory); /** * @dev Get supported circuit ids @@ -186,14 +160,10 @@ abstract contract CredentialAtomicQueryValidatorBase is */ function getVerifierByCircuitId( string memory circuitId - ) public view virtual returns (IVerifier) { + ) public view virtual returns (IGroth16Verifier) { return _getCredentialAtomicQueryValidatorBaseStorage()._circuitIdToVerifier[circuitId]; } - function _getState() internal view returns (IState) { - return _getCredentialAtomicQueryValidatorBaseStorage().state; - } - /** * @dev Get the index of the public input of the circuit by name * @param name Name of the public input @@ -201,7 +171,24 @@ abstract contract CredentialAtomicQueryValidatorBase is */ function inputIndexOf(string memory name) public view virtual returns (uint256) { uint256 index = _getCredentialAtomicQueryValidatorBaseStorage()._inputNameToIndex[name]; - require(index != 0, "Input name not found"); + if (index == 0) { + revert InputNameNotFound(); + } + return --index; // we save 1-based index, but return 0-based + } + + /** + * @dev Get the index of the request param by name + * @param name Name of the request param + * @return Index of the request param + */ + function requestParamIndexOf(string memory name) public view override returns (uint256) { + uint256 index = _getCredentialAtomicQueryValidatorBaseStorage()._requestParamNameToIndex[ + name + ]; + if (index == 0) { + revert RequestParamNameNotFound(); + } return --index; // we save 1-based index, but return 0-based } @@ -210,40 +197,54 @@ 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); } - function _checkGistRoot(uint256 _id, uint256 _gistRoot, IState _stateContract) internal view { + function _initDefaultStateVariables( + address _stateContractAddr, + address _verifierContractAddr, + string memory circuitId, + address owner + ) internal { + CredentialAtomicQueryValidatorBaseStorage + storage s = _getCredentialAtomicQueryValidatorBaseStorage(); + + 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); + } + + function _checkGistRoot(uint256 _id, uint256 _gistRoot) internal view { CredentialAtomicQueryValidatorBaseStorage storage $ = _getCredentialAtomicQueryValidatorBaseStorage(); bytes2 idType = GenesisUtils.getIdType(_id); - uint256 replacedAt = _stateContract.getGistRootReplacedAt(idType, _gistRoot); + uint256 replacedAt = $.state.getGistRootReplacedAt(idType, _gistRoot); if (replacedAt != 0 && block.timestamp > $.gistRootExpirationTimeout + replacedAt) { - revert("Gist root is expired"); + revert GistRootIsExpired(); } } - function _checkClaimIssuanceState( - uint256 _id, - uint256 _state, - IState _stateContract - ) internal view { - _stateContract.getStateReplacedAt(_id, _state); + function _getState() internal view returns (IState) { + return _getCredentialAtomicQueryValidatorBaseStorage().state; + } + + function _checkClaimIssuanceState(uint256 _id, uint256 _state) internal view { + _getState().getStateReplacedAt(_id, _state); } - function _checkClaimNonRevState( - uint256 _id, - uint256 _claimNonRevState, - IState _stateContract - ) internal view { + function _checkClaimNonRevState(uint256 _id, uint256 _claimNonRevState) internal view { CredentialAtomicQueryValidatorBaseStorage storage $ = _getCredentialAtomicQueryValidatorBaseStorage(); - uint256 replacedAt = _stateContract.getStateReplacedAt(_id, _claimNonRevState); + uint256 replacedAt = _getState().getStateReplacedAt(_id, _claimNonRevState); if (replacedAt != 0 && block.timestamp > $.revocationStateExpirationTimeout + replacedAt) { - revert("Non-Revocation state of Issuer expired"); + revert NonRevocationStateOfIssuerIsExpired(); } } @@ -254,14 +255,14 @@ abstract contract CredentialAtomicQueryValidatorBase is https://github.com/ethereum/go-ethereum/issues/24152 */ if (_proofGenerationTimestamp > (block.timestamp + 5 minutes)) { - revert("Proof generated in the future is not valid"); + revert ProofGeneratedInTheFutureIsNotValid(); } if ( block.timestamp > _getCredentialAtomicQueryValidatorBaseStorage().proofExpirationTimeout + _proofGenerationTimestamp ) { - revert("Generated proof is outdated"); + revert GeneratedProofIsOutdated(); } } @@ -271,24 +272,30 @@ abstract contract CredentialAtomicQueryValidatorBase is return; } - for (uint i = 0; i < allowedIssuers.length; i++) { + for (uint256 i = 0; i < allowedIssuers.length; i++) { if (issuerId == allowedIssuers[i]) { return; } } - revert("Issuer is not on the Allowed Issuers list"); + revert IssuerIsNotOnTheAllowedIssuersList(); } function _checkChallenge(uint256 challenge, address sender) internal pure { - require( - PrimitiveTypeUtils.uint256LEToAddress(challenge) == sender, - "Challenge should match the sender" - ); + if (PrimitiveTypeUtils.uint256LEToAddress(challenge) != sender) { + revert ChallengeShouldMatchTheSender(); + } } function _setInputToIndex(string memory inputName, uint256 index) internal { // increment index to avoid 0 _getCredentialAtomicQueryValidatorBaseStorage()._inputNameToIndex[inputName] = ++index; } + + function _setRequestParamToIndex(string memory requestParamName, uint256 index) internal { + // increment index to avoid 0 + _getCredentialAtomicQueryValidatorBaseStorage()._requestParamNameToIndex[ + requestParamName + ] = ++index; + } } diff --git a/contracts/validators/request/LinkedMultiQueryValidator.sol b/contracts/validators/request/LinkedMultiQueryValidator.sol new file mode 100644 index 00000000..60e0b5d3 --- /dev/null +++ b/contracts/validators/request/LinkedMultiQueryValidator.sol @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.10; + +import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {IGroth16Verifier} from "../../interfaces/IGroth16Verifier.sol"; +import {IRequestValidator} from "../../interfaces/IRequestValidator.sol"; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; + +error WrongCircuitID(string circuitID); +error InvalidQueryHash(uint256 expectedQueryHash, uint256 actualQueryHash); +error InvalidGroupID(uint256 groupID); +error TooManyQueries(uint256 operatorCount); +error InvalidGroth16Proof(); +error RequestParamNameNotFound(); + +contract LinkedMultiQueryValidator is Ownable2StepUpgradeable, IRequestValidator, ERC165 { + // 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; + uint256 groupID; + uint256 verifierID; + } + + /// @dev Main storage structure for the contract + /// @custom:storage-location iden3.storage.LinkedMultiQueryValidatorStorage + struct LinkedMultiQueryValidatorStorage { + mapping(string circuitName => IGroth16Verifier) _supportedCircuits; + string[] _supportedCircuitIds; + mapping(string => uint256) _requestParamNameToIndex; + } + + // keccak256(abi.encode(uint256(keccak256("iden3.storage.LinkedMultiQueryValidator")) - 1)) + // & ~bytes32(uint256(0xff)); + // solhint-disable-next-line const-name-snakecase + bytes32 private constant LinkedMultiQueryValidatorStorageLocation = + 0x85875fc21d0742149175681df1689e48bce1484a73b475e15e5042650a2d7800; + + /// @dev Get the main storage using assembly to ensure specific storage location + function _getLinkedMultiQueryValidatorStorage() + private + pure + returns (LinkedMultiQueryValidatorStorage storage $) + { + // solhint-disable-next-line no-inline-assembly + assembly { + $.slot := LinkedMultiQueryValidatorStorageLocation + } + } + + struct PubSignals { + uint256 linkID; + uint256 merklized; + uint256[10] operatorOutput; + uint256[10] circuitQueryHash; + } + + string public constant VERSION = "1.0.0-beta.1"; + string internal constant CIRCUIT_ID = "linkedMultiQuery10-beta.1"; + uint256 internal constant QUERIES_COUNT = 10; + + /** + * @dev Returns the version of the contract + * @return The version of the contract + */ + function version() external pure override returns (string memory) { + return VERSION; + } + + /** + * @dev Initialize the contract + * @param _groth16VerifierContractAddr Address of the verifier contract + * @param owner Owner of the contract + */ + function initialize(address _groth16VerifierContractAddr, address owner) public initializer { + LinkedMultiQueryValidatorStorage storage $ = _getLinkedMultiQueryValidatorStorage(); + $._supportedCircuits[CIRCUIT_ID] = IGroth16Verifier(_groth16VerifierContractAddr); + $._supportedCircuitIds.push(CIRCUIT_ID); + + _setRequestParamToIndex("groupID", 0); + _setRequestParamToIndex("verifierID", 1); + _setRequestParamToIndex("nullifierSessionID", 2); + + __Ownable_init(owner); + } + + /** + * @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 sender Sender of the proof. + * @param proof Proof packed as bytes to verify. + * @param params Request query data of the credential to verify. + * @return Array of response fields as result. + */ + function verify( + // solhint-disable-next-line no-unused-vars + address sender, + bytes calldata proof, + bytes calldata params + ) external view returns (IRequestValidator.ResponseField[] memory) { + LinkedMultiQueryValidatorStorage storage $ = _getLinkedMultiQueryValidatorStorage(); + + Query memory query = abi.decode(params, (Query)); + ( + 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); + + _checkQueryHash(query, pubSignals); + _checkGroupId(query.groupID); + + if (keccak256(bytes(query.circuitIds[0])) != keccak256(bytes(CIRCUIT_ID))) { + revert WrongCircuitID(query.circuitIds[0]); + } + if (!$._supportedCircuits[CIRCUIT_ID].verify(a, b, c, inputs)) { + revert InvalidGroth16Proof(); + } + + return _getResponseFields(pubSignals, query); + } + + /** + * @dev Decodes special request parameters from the request params + * do be used by upper level clients of this contract. + * @param params Request parameters packed as bytes. + * @return Special request parameters extracted from the request data. + */ + function getRequestParams( + bytes calldata params + ) external pure override returns (IRequestValidator.RequestParam[] memory) { + Query memory query = abi.decode(params, (Query)); + IRequestValidator.RequestParam[] + memory requestParams = new IRequestValidator.RequestParam[](3); + requestParams[0] = IRequestValidator.RequestParam({name: "groupID", value: query.groupID}); + requestParams[1] = IRequestValidator.RequestParam({ + name: "verifierID", + value: query.verifierID + }); + requestParams[2] = IRequestValidator.RequestParam({name: "nullifierSessionID", value: 0}); + return requestParams; + } + + /** + * @dev Get the index of the request param by name + * @param name Name of the request param + * @return Index of the request param + */ + function requestParamIndexOf( + string memory name + ) public view virtual override returns (uint256) { + uint256 index = _getLinkedMultiQueryValidatorStorage()._requestParamNameToIndex[name]; + if (index == 0) { + revert RequestParamNameNotFound(); + } + return --index; // we save 1-based index, but return 0-based + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return + interfaceId == type(IRequestValidator).interfaceId || + super.supportsInterface(interfaceId); + } + + function _checkGroupId(uint256 groupID) internal pure { + if (groupID == 0) { + revert InvalidGroupID(groupID); + } + } + + function _checkQueryHash(Query memory query, PubSignals memory pubSignals) internal pure { + if (query.queryHash.length > QUERIES_COUNT) { + revert TooManyQueries(query.queryHash.length); + } + for (uint256 i = 0; i < query.queryHash.length; i++) { + if (query.queryHash[i] != pubSignals.circuitQueryHash[i]) { + revert InvalidQueryHash(query.queryHash[i], pubSignals.circuitQueryHash[i]); + } + } + } + + function _parsePubSignals(uint256[] memory inputs) internal pure returns (PubSignals memory) { + uint256[QUERIES_COUNT] memory opsOutput; + uint256[QUERIES_COUNT] memory queryHashes; + PubSignals memory pubSignals = PubSignals({ + linkID: 0, + merklized: 0, + operatorOutput: opsOutput, + circuitQueryHash: queryHashes + }); + + pubSignals.linkID = inputs[0]; + pubSignals.merklized = inputs[1]; + for (uint256 i = 0; i < QUERIES_COUNT; i++) { + pubSignals.operatorOutput[i] = inputs[2 + i]; + pubSignals.circuitQueryHash[i] = inputs[2 + QUERIES_COUNT + i]; + } + return pubSignals; + } + + function _getResponseFields( + 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. Check via gasleft() first + if (query.operator[i] == 16) { + rfs[m++] = ResponseField( + string(abi.encodePacked("operatorOutput", Strings.toString(i))), + pubSignals.operatorOutput[i] + ); + } + } + + return rfs; + } + + function _setRequestParamToIndex(string memory requestParamName, uint256 index) internal { + // increment index to avoid 0 + _getLinkedMultiQueryValidatorStorage()._requestParamNameToIndex[requestParamName] = ++index; + } +} diff --git a/contracts/verifiers/EmbeddedVerifier.sol b/contracts/verifiers/EmbeddedVerifier.sol new file mode 100644 index 00000000..95fea945 --- /dev/null +++ b/contracts/verifiers/EmbeddedVerifier.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {Verifier} from "./Verifier.sol"; +import {IState} from "../interfaces/IState.sol"; + +abstract contract EmbeddedVerifier is Ownable2StepUpgradeable, Verifier { + /// @dev Sets the state contract linked to this verifier + /// @param state The state contract address + function setState(IState state) public onlyOwner { + _setState(state); + } + + /** + * @dev Submits an array of responses and updates proofs status + * @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 authResponse, + Response[] memory responses, + bytes memory crossChainProofs + ) public virtual override { + _beforeProofSubmit(authResponse, responses); + super.submitResponse(authResponse, responses, crossChainProofs); + _afterProofSubmit(authResponse, responses); + } + + /** + * @dev Sets the value for Owner + */ + // solhint-disable-next-line func-name-mixedcase + function __EmbeddedVerifier_init(address initialOwner, IState state) internal onlyInitializing { + __Ownable_init(initialOwner); + ___EmbeddedVerifier_init_unchained(initialOwner); + __Verifier_init(state); + } + + /* solhint-disable no-empty-blocks */ + // solhint-disable-next-line func-name-mixedcase + function ___EmbeddedVerifier_init_unchained(address initialOwner) internal onlyInitializing {} + + /** + * @dev Hook that is called before any proof response submit + * @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 authResponse, + Response[] memory responses + ) internal virtual {} + + /** + * @dev Hook that is called after any proof response submit + * @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 authResponse, + Response[] memory responses + ) internal virtual {} + /* solhint-enable no-empty-blocks */ +} diff --git a/contracts/verifiers/EmbeddedZKPVerifier.sol b/contracts/verifiers/EmbeddedZKPVerifier.sol deleted file mode 100644 index 64268afa..00000000 --- a/contracts/verifiers/EmbeddedZKPVerifier.sol +++ /dev/null @@ -1,118 +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 Set the list of ZKP requests for the list of requestIds in the same order. - * @param requestIds Request ids of the ZKP requests. - * @param requests ZKP requests to set. - */ - function setZKPRequests( - uint64[] calldata requestIds, - ZKPRequest[] calldata requests - ) public virtual override onlyOwner { - super.setZKPRequests(requestIds, requests); - } - - /// @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 7ff1dfa0..988a8f49 100644 --- a/contracts/verifiers/RequestDisableable.sol +++ b/contracts/verifiers/RequestDisableable.sol @@ -1,82 +1,65 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; -import {ZKPVerifierBase} from "./ZKPVerifierBase.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; -import {IZKPVerifier} from "../interfaces/IZKPVerifier.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)); + // solhint-disable-next-line const-name-snakecase bytes32 private constant RequestDisableStorageLocation = 0x70325635d67d74932012fa921ccb2f335d3b1d69e3a487f50d001cc65f531600; function _getRequestDisableStorage() private pure returns (RequestDisableStorage storage $) { + // solhint-disable-next-line no-inline-assembly assembly { $.slot := RequestDisableStorageLocation } } - /// @dev Modifier to check if the ZKP request is enabled - modifier onlyEnabledRequest(uint64 requestId) { - require(isZKPRequestEnabled(requestId), "Request is disabled"); + /// @dev Modifier to check if the request is enabled + modifier onlyEnabledRequest(uint256 requestId) { + if (!isRequestEnabled(requestId)) { + revert RequestIsDisabled(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); - } - - /// @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; } function _getRequestIfCanBeVerified( - uint64 requestId + uint256 requestId ) internal view virtual override onlyEnabledRequest(requestId) - returns (IZKPVerifier.ZKPRequest storage) + returns (IVerifier.RequestData storage) { return super._getRequestIfCanBeVerified(requestId); } diff --git a/contracts/verifiers/RequestOwnership.sol b/contracts/verifiers/RequestOwnership.sol index 194c83d6..397d4ad1 100644 --- a/contracts/verifiers/RequestOwnership.sol +++ b/contracts/verifiers/RequestOwnership.sol @@ -1,16 +1,17 @@ // 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)); + // solhint-disable-next-line const-name-snakecase bytes32 private constant RequestOwnershipStorageLocation = 0x6209bdc3799f5201408f7a7d4d471bb2a0100353e618451674b93f730b006a00; @@ -19,53 +20,30 @@ abstract contract RequestOwnership is ZKPVerifierBase { pure returns (RequestOwnershipStorage storage $) { + // solhint-disable-next-line no-inline-assembly assembly { $.slot := RequestOwnershipStorageLocation } } - /// @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 - ) public virtual override { - super.setZKPRequest(requestId, request); - _setRequestOwner(requestId, _msgSender()); - } - /** - * @dev Set the list of ZKP requests for the list of requestIds in the same order. - * @param requestIds Request ids of the ZKP requests. - * @param requests ZKP requests to set. + * @dev Get a request owner address + * @param requestId The ID of a request + * @return The request owner address */ - function setZKPRequests( - uint64[] calldata requestIds, - ZKPRequest[] calldata requests - ) public virtual override { - for (uint256 i = 0; i < requestIds.length; i++) { - setZKPRequest(requestIds[i], requests[i]); - } - } - - /// @dev Get a ZKP Request Owner address - /// @param requestId The ID of a ZKP Request - /// @return The ZKP Request Owner address function getRequestOwner( - uint64 requestId + uint256 requestId ) public view virtual checkRequestExistence(requestId, true) returns (address) { return _getRequestOwnershipStorage()._requestOwners[requestId]; } + function _setRequest(IVerifier.Request calldata request) internal virtual override { + super._setRequest(request); + _setRequestOwner(request.requestId, _msgSender()); + } + 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 6d5882c3..2cdf229b 100644 --- a/contracts/verifiers/UniversalVerifier.sol +++ b/contracts/verifiers/UniversalVerifier.sol @@ -2,14 +2,16 @@ 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"; +error NotAnOwnerOrRequestOwner(address); + /// @title Universal Verifier Contract /// @notice A contract to manage ZKP (Zero-Knowledge Proof) requests and proofs. contract UniversalVerifier is @@ -21,43 +23,63 @@ contract UniversalVerifier is /** * @dev Version of contract */ - string public constant VERSION = "1.1.5"; + 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 adding a ZKP request - event ZKPRequestSet( - uint64 indexed requestId, + /** + * @dev Event emitted upon submitting an auth response + */ + event AuthResponseSubmitted(string indexed authMethod, address indexed caller); + + /** + * @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 method by the owner + */ + event AuthMethodSet(string indexed authMethod, 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 Event emitted upon adding a multiRequest + */ + event MultiRequestSet(uint256 indexed multiRequestId, uint256[] requestIds, uint256[] groupIds); + /// @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(), - "Not an owner or request owner" - ); + if (sender != getRequestOwner(requestId) && sender != owner()) { + revert NotAnOwnerOrRequestOwner(sender); + } _; } /// @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,111 +87,66 @@ 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); - - emit ZKPRequestSet( - requestId, - _msgSender(), - request.metadata, - address(request.validator), - request.data - ); - } - /** - * @dev Set the list of ZKP requests for the list of requestIds in the same order. - * @param requestIds Request ids of the ZKP requests. - * @param requests ZKP requests to set. + * @dev Sets an auth type + * @param authMethod The auth type to add */ - function setZKPRequests( - uint64[] calldata requestIds, - ZKPRequest[] calldata requests - ) public override(RequestOwnership, ValidatorWhitelist, ZKPVerifierBase) { - for (uint256 i = 0; i < requestIds.length; i++) { - setZKPRequest(requestIds[i], requests[i]); - } + function setAuthMethod(IVerifier.AuthMethod calldata authMethod) public override onlyOwner { + super.setAuthMethod(authMethod); + emit AuthMethodSet(authMethod.authMethod, address(authMethod.validator), authMethod.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 request The request data + */ + function updateRequest(IVerifier.Request calldata request) public onlyOwner { + super._updateRequest(request); - emit ZKPRequestUpdate( - requestId, + emit RequestUpdate( + request.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 { - super.submitZKPResponse(requestId, inputs, a, b, c); - emit ZKPResponseSubmitted(requestId, _msgSender()); + /** + * @dev Sets a multiRequest + * @param multiRequest The multiRequest data + */ + function setMultiRequest( + IVerifier.MultiRequest calldata multiRequest + ) public override checkMultiRequestExistence(multiRequest.multiRequestId, false) { + super.setMultiRequest(multiRequest); + emit MultiRequestSet( + multiRequest.multiRequestId, + multiRequest.requestIds, + multiRequest.groupIds + ); } /** - * @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 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 submitZKPResponseV2( - IZKPVerifier.ZKPResponse[] memory responses, - bytes memory crossChainProof + function submitResponse( + AuthResponse memory authResponse, + Response[] memory responses, + bytes memory crossChainProofs ) public override { - super.submitZKPResponseV2(responses, crossChainProof); + super.submitResponse(authResponse, responses, crossChainProofs); + emit AuthResponseSubmitted(authResponse.authMethod, _msgSender()); + for (uint256 i = 0; i < responses.length; i++) { - emit ZKPResponseSubmitted(responses[i].requestId, _msgSender()); + emit ResponseSubmitted(responses[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); - } - /** * @dev Sets the state contract address */ @@ -177,60 +154,78 @@ 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 the verifier ID + */ + function setVerifierID(uint256 verifierID) public onlyOwner { + _setVerifierID(verifierID); } - /// @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); } function _getRequestIfCanBeVerified( - uint64 requestId + uint256 requestId ) internal view - override(RequestDisableable, ValidatorWhitelist, ZKPVerifierBase) - onlyEnabledRequest(requestId) - returns (IZKPVerifier.ZKPRequest storage) + override(RequestDisableable, ValidatorWhitelist, Verifier) + returns (IVerifier.RequestData storage) { 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 49b619d0..5d2dc09c 100644 --- a/contracts/verifiers/ValidatorWhitelist.sol +++ b/contracts/verifiers/ValidatorWhitelist.sol @@ -2,17 +2,21 @@ 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); +error ValidatorNotSupportInterface(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)); + // solhint-disable-next-line const-name-snakecase bytes32 private constant ValidatorWhitelistStorageLocation = 0x76aa24e3538905838cc74060b2aa4c054b1e474aacf44741879e1850715e9300; @@ -21,93 +25,61 @@ contract ValidatorWhitelist is ZKPVerifierBase { pure returns (ValidatorWhitelistStorage storage $) { + // solhint-disable-next-line no-inline-assembly assembly { $.slot := ValidatorWhitelistStorageLocation } } /// @dev Modifier to check if the validator is whitelisted - modifier onlyWhitelistedValidator(ICircuitValidator validator) { - require(isWhitelistedValidator(validator), "Validator is not whitelisted"); + modifier onlyWhitelistedValidator(IRequestValidator 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); - } - /** - * @dev Set the list of ZKP requests for the list of requestIds in the same order. - * @param requestIds Request ids of the ZKP requests. - * @param requests ZKP requests to set. + * @dev Checks if validator is whitelisted + * @param validator The validator address + * @return True if validator is whitelisted, otherwise returns false */ - function setZKPRequests( - uint64[] calldata requestIds, - ZKPRequest[] calldata requests - ) public virtual override { - for (uint256 i = 0; i < requestIds.length; i++) { - setZKPRequest(requestIds[i], requests[i]); - } - } - - /// @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); - } - - /// @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 { - require( - IERC165(address(validator)).supportsInterface(type(ICircuitValidator).interfaceId), - "Validator doesn't support relevant interface" - ); + function _addValidatorToWhitelist(IRequestValidator validator) internal { + if (!IERC165(address(validator)).supportsInterface(type(IRequestValidator).interfaceId)) { + revert ValidatorNotSupportInterface(address(validator)); + } _getValidatorWhitelistStorage()._validatorWhitelist[validator] = true; } - function _removeValidatorFromWhitelist(ICircuitValidator validator) internal { + function _removeValidatorFromWhitelist(IRequestValidator validator) internal { _getValidatorWhitelistStorage()._validatorWhitelist[validator] = false; } function _getRequestIfCanBeVerified( - uint64 requestId + uint256 requestId ) internal view virtual override - onlyWhitelistedValidator(getZKPRequest(requestId).validator) - returns (IZKPVerifier.ZKPRequest storage) + onlyWhitelistedValidator(getRequest(requestId).validator) + returns (IVerifier.RequestData storage) { 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 new file mode 100644 index 00000000..217d9d37 --- /dev/null +++ b/contracts/verifiers/Verifier.sol @@ -0,0 +1,976 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +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 {IVerifier} from "../interfaces/IVerifier.sol"; +import {GenesisUtils} from "../lib/GenesisUtils.sol"; + +error AuthMethodNotFound(string authMethod); +error AuthMethodAlreadyExists(string authMethod); +error GroupIdNotFound(uint256 groupId); +error GroupIdAlreadyExists(uint256 groupId); +error GroupMustHaveAtLeastTwoRequests(uint256 groupID); +error LinkIDNotTheSameForGroupedRequests(); +error MetadataNotSupportedYet(); +error MultiRequestIdAlreadyExists(uint256 multiRequestId); +error MultiRequestIdNotFound(uint256 multiRequestId); +error NullifierSessionIDAlreadyExists(uint256 nullifierSessionID); +error ResponseFieldAlreadyExists(string responseFieldName); +error RequestIdAlreadyExists(uint256 requestId); +error RequestIdNotFound(uint256 requestId); +error RequestIdNotValid(); +error RequestIdUsesReservedBytes(); +error RequestIdTypeNotValid(); +error RequestShouldNotHaveAGroup(uint256 requestId); +error UserIDMismatch(uint256 userIDFromAuth, uint256 userIDFromResponse); +error UserIDNotFound(uint256 userID); +error UserIDNotLinkedToAddress(uint256 userID, address userAddress); +error UserNotAuthenticated(); +error ValidatorNotWhitelisted(address validator); +error VerifierIDIsNotValid(uint256 requestVerifierID, uint256 expectedVerifierID); +error ChallengeIsInvalid(); + +abstract contract Verifier is IVerifier, ContextUpgradeable { + /// @dev Key to retrieve the linkID from the proof storage + string private constant LINKED_PROOF_KEY = "linkID"; + // keccak256(abi.encodePacked("authV2")) + bytes32 private constant AUTHV2_NAME = + 0x380ee2d21c7a4607d113dad9e76a0bc90f5325a136d5f0e14b6ccf849d948e25; + // keccak256(abi.encodePacked("challenge")) + bytes32 private constant CHALLENGE_NAME = + 0x62357b294ca756256b576c5da68950c49d0d1823063551ffdcc1dad9d65a07a6; + + struct AuthMethodData { + IAuthValidator validator; + bytes params; + bool isActive; + } + + /// @custom:storage-location erc7201:iden3.storage.Verifier + struct VerifierStorage { + // Information about requests + // solhint-disable-next-line + mapping(uint256 requestId => mapping(address sender => Proof)) _proofs; + mapping(uint256 requestId => IVerifier.RequestData) _requests; + uint256[] _requestIds; + IState _state; + mapping(uint256 groupId => uint256[] requestIds) _groupedRequests; + uint256[] _groupIds; + // Information about multiRequests + mapping(uint256 multiRequestId => IVerifier.MultiRequest) _multiRequests; + uint256[] _multiRequestIds; + // Information about auth methods and validators + string[] _authMethodsNames; + mapping(string authMethod => AuthMethodData) _authMethods; + mapping(uint256 nullifierSessionID => uint256 requestId) _nullifierSessionIDs; + // verifierID to check in requests + uint256 _verifierID; + } + + /** + * @dev Struct to store proof and associated data + */ + struct Proof { + bool isVerified; + mapping(string key => uint256 inputValue) storageFields; + string validatorVersion; + uint256 blockTimestamp; + string[] storageFieldNames; + // introduce artificial shift + 1 to avoid 0 index + mapping(string key => uint256 keyIndex) storageFieldIndexes; + uint256[44] __gap; + } + + // keccak256(abi.encode(uint256(keccak256("iden3.storage.Verifier")) -1 )) & ~bytes32(uint256(0xff)); + // solhint-disable-next-line const-name-snakecase + bytes32 internal constant VerifierStorageLocation = + 0x11369addde4aae8af30dcf56fa25ad3d864848d3201d1e9197f8b4da18a51a00; + + function _getVerifierStorage() private pure returns (VerifierStorage storage $) { + // solhint-disable-next-line no-inline-assembly + assembly { + $.slot := VerifierStorageLocation + } + } + + bytes2 internal constant VERIFIER_ID_TYPE = 0x01A1; + + /** + * @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 RequestIdAlreadyExists(requestId); + } + } + _; + } + + /** + * @dev Modifier to check if the multiRequest exists + */ + modifier checkMultiRequestExistence(uint256 multiRequestId, bool existence) { + if (existence) { + if (!multiRequestIdExists(multiRequestId)) { + revert MultiRequestIdNotFound(multiRequestId); + } + } else { + if (multiRequestIdExists(multiRequestId)) { + revert MultiRequestIdAlreadyExists(multiRequestId); + } + } + _; + } + + /** + * @dev Modifier to check if the auth type exists + */ + modifier checkAuthMethodExistence(string memory authMethod, bool existence) { + if (existence) { + if (!authMethodExists(authMethod)) { + revert AuthMethodNotFound(authMethod); + } + } else { + if (authMethodExists(authMethod)) { + revert AuthMethodAlreadyExists(authMethod); + } + } + _; + } + + /** + * @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()._groupedRequests[groupId].length != 0; + } + + /** + * @dev Checks if a multiRequest ID exists + * @param multiRequestId The ID of the multiRequest + * @return Whether the multiRequest ID exists + */ + function multiRequestIdExists(uint256 multiRequestId) public view returns (bool) { + return + _getVerifierStorage()._multiRequests[multiRequestId].multiRequestId == multiRequestId; + } + + /** + * @dev Checks if an auth method exists + * @param authMethod The auth method + * @return Whether the auth type exists + */ + function authMethodExists(string memory authMethod) public view returns (bool) { + return + _getVerifierStorage()._authMethods[authMethod].validator != IAuthValidator(address(0)); + } + + /** + * @dev Sets different requests + * @param requests The list of requests + */ + function setRequests(IVerifier.Request[] calldata requests) public { + VerifierStorage storage s = _getVerifierStorage(); + + 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)[ + requests[i].validator.requestParamIndexOf("groupID") + ].value; + + if (groupID != 0) { + if (groupIdExists(groupID)) { + revert GroupIdAlreadyExists(groupID); + } + + (bool exists, uint256 groupIDIndex) = _getGroupIDIndex( + groupID, + newGroupsGroupID, + newGroupsCount + ); + + if (!exists) { + newGroupsGroupID[newGroupsCount] = groupID; + newGroupsRequestCount[newGroupsCount]++; + newGroupsCount++; + } else { + newGroupsRequestCount[groupIDIndex]++; + } + } + } + + _checkGroupsRequestsCount(newGroupsGroupID, newGroupsRequestCount, newGroupsCount); + + // 2. Set requests checking groups and nullifierSessionID uniqueness + for (uint256 i = 0; i < requests.length; i++) { + _checkRequestIdCorrectness(requests[i].requestId, requests[i].params); + + _checkNullifierSessionIdUniqueness(requests[i]); + _checkVerifierID(requests[i]); + + uint256 groupID = requests[i] + .validator + .getRequestParams(requests[i].params)[ + requests[i].validator.requestParamIndexOf("groupID") + ].value; + _setRequest(requests[i]); + + // request with group + if (groupID != 0) { + // request with group + if (!groupIdExists(groupID)) { + s._groupIds.push(groupID); + } + + s._groupedRequests[groupID].push(requests[i].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(); + IVerifier.RequestData storage rd = $._requests[requestId]; + return + IVerifier.RequestInfo({ + requestId: requestId, + metadata: rd.metadata, + validator: rd.validator, + params: rd.params, + creator: rd.creator + }); + } + + /** + * @dev Sets a multiRequest + * @param multiRequest The multiRequest data + */ + function setMultiRequest( + IVerifier.MultiRequest calldata multiRequest + ) public virtual checkMultiRequestExistence(multiRequest.multiRequestId, false) { + VerifierStorage storage s = _getVerifierStorage(); + s._multiRequests[multiRequest.multiRequestId] = multiRequest; + s._multiRequestIds.push(multiRequest.multiRequestId); + + // checks for all the requests in this multiRequest + _checkRequestsInMultiRequest(multiRequest.multiRequestId); + } + + /** + * @dev Gets a specific multiRequest by ID + * @param multiRequestId The ID of the multiRequest + * @return multiRequest The multiRequest data + */ + function getMultiRequest( + uint256 multiRequestId + ) + public + view + checkMultiRequestExistence(multiRequestId, true) + returns (IVerifier.MultiRequest memory multiRequest) + { + return _getVerifierStorage()._multiRequests[multiRequestId]; + } + + /** + * @dev Submits an array of responses and updates proofs status + * @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 authResponse, + Response[] memory responses, + bytes memory crossChainProofs + ) public virtual { + VerifierStorage storage $ = _getVerifierStorage(); + address sender = _msgSender(); + + // 1. Process crossChainProofs + $._state.processCrossChainProofs(crossChainProofs); + + uint256 userIDFromAuthResponse; + AuthMethodData storage authMethodData = $._authMethods[authResponse.authMethod]; + + // 2. Authenticate user and get userID + IAuthValidator.AuthResponseField[] memory authResponseFields; + (userIDFromAuthResponse, authResponseFields) = authMethodData.validator.verify( + sender, + authResponse.proof, + authMethodData.params + ); + + if (keccak256(abi.encodePacked(authResponse.authMethod)) == AUTHV2_NAME) { + if ( + authResponseFields.length > 0 && + keccak256(abi.encodePacked(authResponseFields[0].name)) == CHALLENGE_NAME + ) { + bytes32 expectedNonce = keccak256(abi.encode(sender, responses)) & + 0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + if (expectedNonce != bytes32(authResponseFields[0].value)) { + revert ChallengeIsInvalid(); + } + } + } + + if (userIDFromAuthResponse == 0) { + revert UserNotAuthenticated(); + } + + // 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]; + IVerifier.RequestData storage request = _getRequestIfCanBeVerified(response.requestId); + + IRequestValidator.ResponseField[] memory signals = request.validator.verify( + sender, + response.proof, + request.params + ); + + // Check if userID from authResponse is the same as the one in the signals + _checkUserIDMatch(userIDFromAuthResponse, signals); + + _writeProofResults(response.requestId, sender, signals); + + if (response.metadata.length > 0) { + revert MetadataNotSupportedYet(); + } + } + } + + /** + * @dev Sets an auth method + * @param authMethod The auth method to add + */ + function setAuthMethod( + IVerifier.AuthMethod calldata authMethod + ) public virtual checkAuthMethodExistence(authMethod.authMethod, false) { + VerifierStorage storage s = _getVerifierStorage(); + s._authMethodsNames.push(authMethod.authMethod); + s._authMethods[authMethod.authMethod] = AuthMethodData({ + validator: authMethod.validator, + params: authMethod.params, + isActive: true + }); + } + + /** + * @dev Disables an auth method + * @param authMethod The auth method to disable + */ + function disableAuthMethod( + string calldata authMethod + ) public checkAuthMethodExistence(authMethod, true) { + VerifierStorage storage s = _getVerifierStorage(); + s._authMethods[authMethod].isActive = false; + } + + /** + * @dev Enables an auth type + * @param authMethod The auth type to enable + */ + function enableAuthMethod( + string calldata authMethod + ) public checkAuthMethodExistence(authMethod, true) { + VerifierStorage storage s = _getVerifierStorage(); + s._authMethods[authMethod].isActive = true; + } + + /** + * @dev Gets an auth type + * @param authMethod The Id of the auth type to get + * @return authMethodData The auth type data + */ + function getAuthMethod( + string calldata authMethod + ) + public + view + checkAuthMethodExistence(authMethod, true) + returns (AuthMethodData memory authMethodData) + { + return _getVerifierStorage()._authMethods[authMethod]; + } + + /** + * @dev Gets response field value + * @param requestId Id of the request + * @param sender Address of the user + * @param responseFieldName Name of the response field to get + */ + function getResponseFieldValue( + uint256 requestId, + address sender, + string memory responseFieldName + ) public view checkRequestExistence(requestId, true) returns (uint256) { + VerifierStorage storage s = _getVerifierStorage(); + return s._proofs[requestId][sender].storageFields[responseFieldName]; + } + + /** + * @dev Gets proof storage response fields + * @param requestId Id of the request + * @param sender Address of the user + */ + function getResponseFields( + uint256 requestId, + address sender + ) public view returns (IRequestValidator.ResponseField[] memory) { + VerifierStorage storage s = _getVerifierStorage(); + Proof storage proof = s._proofs[requestId][sender]; + + IRequestValidator.ResponseField[] + memory responseFields = new IRequestValidator.ResponseField[]( + proof.storageFieldNames.length + ); + + for (uint256 i = 0; i < proof.storageFieldNames.length; i++) { + responseFields[i] = IRequestValidator.ResponseField({ + name: proof.storageFieldNames[i], + value: proof.storageFields[proof.storageFieldNames[i]] + }); + } + + return responseFields; + } + + /** + * @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 multiRequest. "True" if all requests are verified, "false" otherwise + */ + function getMultiRequestProofsStatus( + uint256 multiRequestId, + address userAddress + ) + public + view + checkMultiRequestExistence(multiRequestId, true) + returns (IVerifier.RequestProofStatus[] memory) + { + // 1. Check if all requests statuses are true for the userAddress + IVerifier.RequestProofStatus[] memory requestProofStatus = _getMultiRequestProofsStatus( + multiRequestId, + userAddress + ); + + // 2. Check if all linked response fields are the same + bool linkedResponsesOK = _checkLinkedResponseFields(multiRequestId, userAddress); + + if (!linkedResponsesOK) { + revert LinkIDNotTheSameForGroupedRequests(); + } + + return requestProofStatus; + } + + /** + * @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 status The status of the multiRequest. "True" if all requests are verified, "false" otherwise + */ + function areMultiRequestProofsVerified( + uint256 multiRequestId, + address userAddress + ) public view checkMultiRequestExistence(multiRequestId, true) returns (bool) { + // 1. Check if all requests are verified for the userAddress + bool verified = _areMultiRequestProofsVerified(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; + } + + /** + * @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 isRequestProofVerified( + address sender, + uint256 requestId + ) public view checkRequestExistence(requestId, true) returns (bool) { + VerifierStorage storage s = _getVerifierStorage(); + return s._proofs[requestId][sender].isVerified; + } + + /** + * @dev Get the requests count. + * @return Requests count. + */ + function getRequestsCount() public view returns (uint256) { + 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 (IVerifier.RequestInfo[] memory) { + VerifierStorage storage s = _getVerifierStorage(); + + IVerifier.RequestInfo[] memory requests = new IVerifier.RequestInfo[]( + s._groupedRequests[groupID].length + ); + + for (uint256 i = 0; i < s._groupedRequests[groupID].length; i++) { + uint256 requestId = s._groupedRequests[groupID][i]; + IVerifier.RequestData storage rd = s._requests[requestId]; + + requests[i] = IVerifier.RequestInfo({ + requestId: requestId, + metadata: rd.metadata, + validator: rd.validator, + params: rd.params, + creator: rd.creator + }); + } + + return requests; + } + + /** + * @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 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 + * @param requestId The ID of the ZKP request + * @return The proof status structure + */ + function getRequestProofStatus( + address sender, + uint256 requestId + ) + public + view + checkRequestExistence(requestId, true) + returns (IVerifier.RequestProofStatus memory) + { + VerifierStorage storage s = _getVerifierStorage(); + Proof storage proof = s._proofs[requestId][sender]; + + return + IVerifier.RequestProofStatus( + requestId, + proof.isVerified, + proof.validatorVersion, + proof.blockTimestamp + ); + } + + function _setState(IState state) internal { + _getVerifierStorage()._state = state; + } + + // solhint-disable-next-line func-name-mixedcase + function __Verifier_init(IState state) internal onlyInitializing { + __Verifier_init_unchained(state); + } + + // solhint-disable-next-line func-name-mixedcase + function __Verifier_init_unchained(IState state) internal onlyInitializing { + _setState(state); + // initial calculation of verifierID from contract address and verifier id type defined + uint256 calculatedVerifierID = GenesisUtils.calcIdFromEthAddress( + VERIFIER_ID_TYPE, + address(this) + ); + _setVerifierID(calculatedVerifierID); + } + + function _setVerifierID(uint256 verifierID) internal { + VerifierStorage storage s = _getVerifierStorage(); + s._verifierID = verifierID; + } + + function _setRequest( + Request calldata request + ) internal virtual checkRequestExistence(request.requestId, false) { + VerifierStorage storage s = _getVerifierStorage(); + + s._requests[request.requestId] = IVerifier.RequestData({ + metadata: request.metadata, + validator: request.validator, + params: request.params, + creator: _msgSender() + }); + s._requestIds.push(request.requestId); + } + + function _checkVerifierID(IVerifier.Request calldata request) internal view { + VerifierStorage storage s = _getVerifierStorage(); + uint256 requestVerifierID = request + .validator + .getRequestParams(request.params)[request.validator.requestParamIndexOf("verifierID")] + .value; + + if (requestVerifierID != 0) { + if (requestVerifierID != s._verifierID) { + revert VerifierIDIsNotValid(requestVerifierID, s._verifierID); + } + } + } + + function _getRequestType(uint256 requestId) internal pure returns (uint16) { + // 0x0000000000000000 - prefix for old uint64 requests + // 0x0001000000000000 - prefix for keccak256 cut to fit in the remaining 192 bits + return uint16(requestId >> 240); + } + + function _checkRequestIdCorrectness( + uint256 requestId, + bytes calldata requestParams + ) internal pure { + // 1. Check prefix + uint16 requestType = _getRequestType(requestId); + if (requestType >= 2) { + revert RequestIdTypeNotValid(); + } + // 2. Check reserved bytes + if (((requestId << 16) >> 216) > 0) { + revert RequestIdUsesReservedBytes(); + } + // 3. Check if requestId matches the hash of the requestParams + // 0x0000000000000000FFFF...FF. Reserved first 8 bytes for the request Id type and future use + // 0x00010000000000000000...00. First 2 bytes for the request Id type + // - 0x0000... for old request Ids with uint64 + // - 0x0001... for new request Ids with uint256 + if (requestType == 1) { + uint256 hashValue = uint256(keccak256(requestParams)); + if ( + requestId != + (hashValue & 0x0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + + 0x0001000000000000000000000000000000000000000000000000000000000000 + ) { + revert RequestIdNotValid(); + } + } + } + + function _checkNullifierSessionIdUniqueness(IVerifier.Request calldata request) internal { + VerifierStorage storage s = _getVerifierStorage(); + uint256 nullifierSessionID = request + .validator + .getRequestParams(request.params)[ + request.validator.requestParamIndexOf("nullifierSessionID") + ].value; + if (nullifierSessionID != 0) { + if (s._nullifierSessionIDs[nullifierSessionID] != 0) { + revert NullifierSessionIDAlreadyExists(nullifierSessionID); + } + s._nullifierSessionIDs[nullifierSessionID] = nullifierSessionID; + } + } + + 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]); + } + } + } + + function _checkRequestsInMultiRequest(uint256 multiRequestId) internal view { + VerifierStorage storage s = _getVerifierStorage(); + + uint256[] memory requestIds = s._multiRequests[multiRequestId].requestIds; + uint256[] memory groupIds = s._multiRequests[multiRequestId].groupIds; + + // check that all the single requests doesn't have group + for (uint256 i = 0; i < requestIds.length; i++) { + if (!requestIdExists(requestIds[i])) { + revert RequestIdNotFound(requestIds[i]); + } + uint256 groupID = s + ._requests[requestIds[i]] + .validator + .getRequestParams(s._requests[requestIds[i]].params)[ + s._requests[requestIds[i]].validator.requestParamIndexOf("groupID") + ].value; + + if (groupID != 0) { + revert RequestShouldNotHaveAGroup(requestIds[i]); + } + } + + for (uint256 i = 0; i < groupIds.length; i++) { + if (!groupIdExists(groupIds[i])) { + revert GroupIdNotFound(groupIds[i]); + } + } + } + + 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); + } + } + } + } + + /** + * @dev Updates a request + * @param request The request data + */ + function _updateRequest( + IVerifier.Request calldata request + ) internal checkRequestExistence(request.requestId, true) { + VerifierStorage storage s = _getVerifierStorage(); + + s._requests[request.requestId] = IVerifier.RequestData({ + metadata: request.metadata, + validator: request.validator, + params: request.params, + creator: _msgSender() + }); + } + + 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++) { + uint256 groupId = s._multiRequests[multiRequestId].groupIds[i]; + + // Check linkID in the same group or requests is the same + uint256 requestLinkID = getResponseFieldValue( + s._groupedRequests[groupId][0], + sender, + LINKED_PROOF_KEY + ); + for (uint256 j = 1; j < s._groupedRequests[groupId].length; j++) { + uint256 requestLinkIDToCompare = getResponseFieldValue( + s._groupedRequests[groupId][j], + sender, + LINKED_PROOF_KEY + ); + if (requestLinkID != requestLinkIDToCompare) { + return false; + } + } + } + + return true; + } + + function _getMultiRequestProofsStatus( + uint256 multiRequestId, + address userAddress + ) internal view returns (IVerifier.RequestProofStatus[] memory) { + VerifierStorage storage s = _getVerifierStorage(); + IVerifier.MultiRequest storage multiRequest = s._multiRequests[multiRequestId]; + + uint256 lengthGroupIds; + + if (multiRequest.groupIds.length > 0) { + for (uint256 i = 0; i < multiRequest.groupIds.length; i++) { + uint256 groupId = multiRequest.groupIds[i]; + lengthGroupIds += s._groupedRequests[groupId].length; + } + } + + IVerifier.RequestProofStatus[] + memory requestProofStatus = new IVerifier.RequestProofStatus[]( + multiRequest.requestIds.length + lengthGroupIds + ); + + for (uint256 i = 0; i < multiRequest.requestIds.length; i++) { + uint256 requestId = multiRequest.requestIds[i]; + + requestProofStatus[i] = IVerifier.RequestProofStatus({ + requestId: requestId, + isVerified: s._proofs[requestId][userAddress].isVerified, + validatorVersion: s._proofs[requestId][userAddress].validatorVersion, + timestamp: s._proofs[requestId][userAddress].blockTimestamp + }); + } + + 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[multiRequest.requestIds.length + j] = IVerifier + .RequestProofStatus({ + requestId: requestId, + isVerified: s._proofs[requestId][userAddress].isVerified, + validatorVersion: s._proofs[requestId][userAddress].validatorVersion, + timestamp: s._proofs[requestId][userAddress].blockTimestamp + }); + } + } + + return requestProofStatus; + } + + function _areMultiRequestProofsVerified( + 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].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].isVerified) { + return false; + } + } + } + + return true; + } + + function _getRequestIfCanBeVerified( + uint256 requestId + ) + internal + view + virtual + checkRequestExistence(requestId, true) + returns (IVerifier.RequestData storage) + { + VerifierStorage storage $ = _getVerifierStorage(); + return $._requests[requestId]; + } + + /** + * @dev Writes proof results. + * @param requestId The request ID 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( + uint256 requestId, + address sender, + IRequestValidator.ResponseField[] memory responseFields + ) internal { + VerifierStorage storage s = _getVerifierStorage(); + Proof storage proof = s._proofs[requestId][sender]; + + // 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.storageFieldIndexes[responseFields[i].name] == 0) { + proof.storageFieldNames.push(responseFields[i].name); + proof.storageFieldIndexes[responseFields[i].name] = proof.storageFieldNames.length; + } else { + revert ResponseFieldAlreadyExists(responseFields[i].name); + } + } + + proof.isVerified = true; + proof.validatorVersion = s._requests[requestId].validator.version(); + proof.blockTimestamp = block.timestamp; + } +} diff --git a/contracts/verifiers/ZKPVerifier.sol b/contracts/verifiers/ZKPVerifier.sol deleted file mode 100644 index e440d23c..00000000 --- a/contracts/verifiers/ZKPVerifier.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {EmbeddedZKPVerifier} from "./EmbeddedZKPVerifier.sol"; - -/** - * @dev The ZKPVerifier is deprecated and will be removed in the future major versions - * Please use EmbeddedZKPVerifier instead - */ -contract ZKPVerifier is EmbeddedZKPVerifier {} diff --git a/contracts/verifiers/ZKPVerifierBase.sol b/contracts/verifiers/ZKPVerifierBase.sol deleted file mode 100644 index a128d172..00000000 --- a/contracts/verifiers/ZKPVerifierBase.sol +++ /dev/null @@ -1,344 +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); - } - - /** - * @dev Set the ZKP request for the requestId. - * @param requestIds Request ids of the ZKP requests. - * @param requests ZKP requests to set. - */ - function setZKPRequests( - uint64[] calldata requestIds, - ZKPRequest[] calldata requests - ) public virtual { - require(requestIds.length == requests.length, "Request IDs and requests length mismatch"); - - for (uint256 i = 0; i < requestIds.length; i++) { - setZKPRequest(requestIds[i], requests[i]); - } - } - - /// @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 { - address sender = _msgSender(); - ZKPVerifierStorage storage $ = _getZKPVerifierStorage(); - - IZKPVerifier.ZKPRequest storage request = _getRequestIfCanBeVerified(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(); - - IZKPVerifier.ZKPRequest storage request = _getRequestIfCanBeVerified( - 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; - } - - function _getRequestIfCanBeVerified( - uint64 requestId - ) - internal - view - virtual - checkRequestExistence(requestId, true) - returns (IZKPVerifier.ZKPRequest storage) - { - return _getZKPVerifierStorage()._requests[requestId]; - } -} diff --git a/hardhat.config.ts b/hardhat.config.ts index 28646e36..798baa9e 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -41,6 +41,71 @@ const config: HardhatUserConfig = { version: "0.8.27", }, ], + overrides: { + "contracts/verifiers/UniversalVerifier.sol": { + version: "0.8.27", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + "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: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + "contracts/test-helpers/RequestDisableableTestWrapper.sol": { + version: "0.8.27", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + "contracts/test-helpers/RequestOwnershipTestWrapper.sol": { + version: "0.8.27", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + "contracts/test-helpers/ValidatorWhitelistTestWrapper.sol": { + version: "0.8.27", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + "contracts/state/State.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 670d4303..f2a74ce3 100644 --- a/helpers/DeployHelper.ts +++ b/helpers/DeployHelper.ts @@ -23,11 +23,12 @@ import { waitNotToInterfereWithHardhatIgnition, } from "./helperUtils"; import { MCPaymentProxyModule } from "../ignition/modules/mcPayment"; +import { LinkedMultiQueryProxyModule } from "../ignition/modules/linkedMultiQuery"; const SMT_MAX_DEPTH = 64; -export type Groth16VerifierType = "mtpV2" | "sigV2" | "v3" | "authV2"; -export type ValidatorType = "mtpV2" | "sigV2" | "v3" | "authV2"; +export type Groth16VerifierType = "mtpV2" | "sigV2" | "v3" | "authV2" | "lmk10"; +export type ValidatorType = "mtpV2" | "sigV2" | "v3" | "authV2" | "lmk"; export class DeployHelper { constructor( @@ -57,7 +58,6 @@ export class DeployHelper { ): Promise<{ state: Contract; stateLib: Contract; - stateCrossChainLib: Contract; crossChainProofValidator: Contract; smtLib: Contract; poseidon1: Contract; @@ -81,7 +81,6 @@ export class DeployHelper { const { state, stateLib, - stateCrossChainLib, crossChainProofValidator, groth16VerifierStateTransition, defaultIdType, @@ -96,7 +95,6 @@ export class DeployHelper { return { state, stateLib: stateLib!, - stateCrossChainLib: stateCrossChainLib!, crossChainProofValidator: crossChainProofValidator!, defaultIdType, smtLib, @@ -118,7 +116,6 @@ export class DeployHelper { ): Promise<{ state: Contract; stateLib: Contract | null; - stateCrossChainLib: Contract | null; crossChainProofValidator: Contract | null; groth16VerifierStateTransition: Contract | null; defaultIdType; @@ -158,7 +155,6 @@ export class DeployHelper { return { state, stateLib: null, - stateCrossChainLib: null, crossChainProofValidator: null, groth16VerifierStateTransition: null, defaultIdType, @@ -182,23 +178,6 @@ export class DeployHelper { tmpContractDeployments.addContract(contractsInfo.STATE_LIB.name, await stateLib.getAddress()); } - let stateCrossChainLib; - stateCrossChainLib = await tmpContractDeployments.getContract( - contractsInfo.STATE_CROSS_CHAIN_LIB.name, - ); - if (stateCrossChainLib) { - Logger.warning( - `${contractsInfo.STATE_CROSS_CHAIN_LIB.name} found already deployed to: ${await stateCrossChainLib?.getAddress()}`, - ); - } else { - this.log("deploying StateCrossChainLib..."); - stateCrossChainLib = await this.deployStateCrossChainLib("StateCrossChainLib"); - tmpContractDeployments.addContract( - contractsInfo.STATE_CROSS_CHAIN_LIB.name, - await stateCrossChainLib.getAddress(), - ); - } - let crossChainProofValidator; crossChainProofValidator = await tmpContractDeployments.getContract( contractsInfo.CROSS_CHAIN_PROOF_VALIDATOR.name, @@ -223,7 +202,6 @@ export class DeployHelper { StateLib: await stateLib.getAddress(), SmtLib: smtLibAddress, PoseidonUnit1L: poseidon1Address, - StateCrossChainLib: await stateCrossChainLib.getAddress(), }, }); @@ -302,7 +280,6 @@ export class DeployHelper { return { state, stateLib, - stateCrossChainLib, crossChainProofValidator, groth16VerifierStateTransition, defaultIdType, @@ -319,7 +296,6 @@ export class DeployHelper { ): Promise<{ state: Contract; stateLib: Contract; - stateCrossChainLib: Contract; crossChainProofValidator: Contract; }> { this.log("======== State: upgrade started ========"); @@ -330,9 +306,6 @@ export class DeployHelper { this.log("deploying StateLib..."); const stateLib = await this.deployStateLib(); - this.log("deploying StateCrossChainLib..."); - const stateCrossChainLib = await this.deployStateCrossChainLib(); - this.log("upgrading state..."); /* @@ -349,7 +322,6 @@ export class DeployHelper { StateLib: await stateLib.getAddress(), SmtLib: smtLibAddress, PoseidonUnit1L: poseidon1Address, - StateCrossChainLib: await stateCrossChainLib.getAddress(), }, }); @@ -385,7 +357,6 @@ export class DeployHelper { state: stateContract, crossChainProofValidator: opvContract, stateLib, - stateCrossChainLib, }; } @@ -440,14 +411,6 @@ export class DeployHelper { return stateLib; } - async deployStateCrossChainLib(StateCrossChainLibName = "StateCrossChainLib"): Promise { - const stateCrossChainLib = await ethers.deployContract(StateCrossChainLibName); - await stateCrossChainLib.waitForDeployment(); - Logger.success(`StateCrossChainLib deployed to: ${await stateCrossChainLib.getAddress()}`); - - return stateCrossChainLib; - } - async deploySmtLibTestWrapper(maxDepth: number = SMT_MAX_DEPTH): Promise { const contractName = "SmtLibTestWrapper"; @@ -488,17 +451,6 @@ export class DeployHelper { return stateLibWrapper; } - async deployVerifierLib(): Promise { - const contractName = "VerifierLib"; - - const verifierLib = await ethers.deployContract(contractName); - await verifierLib.waitForDeployment(); - - Logger.success(`${contractName} deployed to: ${await verifierLib.getAddress()}`); - - return verifierLib; - } - async deployBinarySearchTestWrapper(): Promise { this.log("deploying poseidons..."); const [poseidon2Elements, poseidon3Elements] = await deployPoseidons([2, 3]); @@ -565,6 +517,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 "lmk": + groth16VerifierType = "lmk10"; + break; + } + return groth16VerifierType; + } + getGroth16VerifierWrapperName(groth16VerifierType: Groth16VerifierType): string { let g16VerifierContractWrapperName; switch (groth16VerifierType) { @@ -580,6 +554,9 @@ export class DeployHelper { case "authV2": g16VerifierContractWrapperName = contractsInfo.GROTH16_VERIFIER_AUTH_V2.name; break; + case "lmk10": + g16VerifierContractWrapperName = contractsInfo.GROTH16_VERIFIER_LINKED_MULTI_QUERY10.name; + break; } return g16VerifierContractWrapperName; } @@ -601,9 +578,12 @@ export class DeployHelper { break; case "v3": verification = contractsInfo.GROTH16_VERIFIER_V3.verificationOpts; + break; case "authV2": verification = contractsInfo.GROTH16_VERIFIER_AUTH_V2.verificationOpts; break; + case "lmk10": + verification = contractsInfo.GROTH16_VERIFIER_LINKED_MULTI_QUERY10.verificationOpts; } return verification; } @@ -625,6 +605,10 @@ export class DeployHelper { break; case "v3": verification = contractsInfo.VALIDATOR_V3.verificationOpts; + break; + case "lmk": + verification = contractsInfo.VALIDATOR_LINKED_MULTI_QUERY.verificationOpts; + break; case "authV2": verification = contractsInfo.VALIDATOR_AUTH_V2.verificationOpts; break; @@ -647,33 +631,32 @@ export class DeployHelper { async deployValidatorContractsWithVerifiers( validatorType: ValidatorType, - stateAddress: string, + stateContractAddress: string, deployStrategy: "basic" | "create2" = "basic", + groth16VerifierWrapperAddress?: string, ): Promise<{ - state: any; groth16VerifierWrapper: any; validator: any; }> { const contracts = await this.deployValidatorContracts( validatorType, - stateAddress, + stateContractAddress, deployStrategy, + groth16VerifierWrapperAddress, ); - const state = await ethers.getContractAt("State", stateAddress); return { validator: contracts.validator, groth16VerifierWrapper: contracts.groth16VerifierWrapper, - state, }; } async deployValidatorContracts( validatorType: ValidatorType, - stateAddress: string, + stateContractAddress: string, deployStrategy: "basic" | "create2" = "basic", + groth16VerifierWrapperAddress?: string, ): Promise<{ - state: any; validator: any; groth16VerifierWrapper: Contract | null; }> { @@ -693,6 +676,8 @@ export class DeployHelper { case "authV2": validatorContractName = "AuthV2Validator"; break; + case "lmk": + validatorContractName = "LinkedMultiQueryValidator"; } let validator; @@ -713,6 +698,9 @@ export class DeployHelper { case "authV2": validatorModule = AuthV2ValidatorProxyModule; break; + case "lmk": + validatorModule = LinkedMultiQueryProxyModule; + break; } await waitNotToInterfereWithHardhatIgnition(undefined); @@ -735,14 +723,25 @@ export class DeployHelper { ); return { validator, - state: await ethers.getContractAt("State", stateAddress), groth16VerifierWrapper: null, }; } } } - 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( @@ -772,36 +771,46 @@ export class DeployHelper { redeployImplementation: "always", call: { fn: "initialize", - args: [await groth16VerifierWrapper.getAddress(), stateAddress, await owner.getAddress()], + args: + validatorType != "lmk" + ? [ + stateContractAddress, + await groth16VerifierWrapper.getAddress(), + await owner.getAddress(), + ] + : [await groth16VerifierWrapper.getAddress(), await owner.getAddress()], }, }); } else { this.log("deploying with BASIC strategy..."); - validator = await upgrades.deployProxy(ValidatorFactory, [ - await groth16VerifierWrapper.getAddress(), - stateAddress, - await owner.getAddress(), - ]); + validator = await upgrades.deployProxy( + ValidatorFactory, + validatorType != "lmk" + ? [ + stateContractAddress, + await groth16VerifierWrapper.getAddress(), + await owner.getAddress(), + ] + : [await groth16VerifierWrapper.getAddress(), await owner.getAddress()], + ); } validator.waitForDeployment(); Logger.success(`${validatorContractName} deployed to: ${await validator.getAddress()}`); - const state = await ethers.getContractAt("State", stateAddress); return { validator, - state, groth16VerifierWrapper, }; } - async deployValidatorStub(): Promise { - const stub = await ethers.getContractFactory("ValidatorStub"); + async deployValidatorStub(validatorName: string = "RequestValidatorStub"): Promise { + const stub = await ethers.getContractFactory(validatorName); const stubInstance = await stub.deploy(); await stubInstance.waitForDeployment(); - console.log("Validator stub deployed to:", await stubInstance.getAddress()); + console.log(`${validatorName} stub deployed to:`, await stubInstance.getAddress()); return stubInstance; } @@ -856,7 +865,6 @@ export class DeployHelper { async upgradeUniversalVerifier( verifierAddress: string, - verifierLibAddr: string, verifierContractName = contractsInfo.UNIVERSAL_VERIFIER.name, ): Promise { this.log("======== Verifier: upgrade started ========"); @@ -865,9 +873,6 @@ export class DeployHelper { this.log("upgrading verifier..."); const VerifierFactory = await ethers.getContractFactory(verifierContractName, { signer: proxyAdminOwner, - libraries: { - VerifierLib: verifierLibAddr, - }, }); this.log("upgrading proxy..."); @@ -908,29 +913,9 @@ export class DeployHelper { return primitiveTypeUtilsWrapper; } - async deployEmbeddedZKPVerifierWrapper( - owner: SignerWithAddress | undefined, - stateAddr: string, - verifierLibAddr: string, - ): Promise { - const Verifier = await ethers.getContractFactory("EmbeddedZKPVerifierWrapper", { - 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("EmbeddedZKPVerifierWrapper deployed to:", await verifier.getAddress()); - return verifier; - } - async deployUniversalVerifier( owner: SignerWithAddress | undefined, stateAddr: string, - verifierLibAddr: string, deployStrategy: "basic" | "create2" = "basic", ): Promise { if (!owner) { @@ -940,9 +925,6 @@ export class DeployHelper { contractsInfo.UNIVERSAL_VERIFIER.name, { signer: owner, - libraries: { - VerifierLib: verifierLibAddr, - }, }, ); const Create2AddressAnchorFactory = await ethers.getContractFactory( diff --git a/helpers/UniversalVerifierContractMigrationHelper.ts b/helpers/UniversalVerifierContractMigrationHelper.ts index aab0623a..3bec09d3 100644 --- a/helpers/UniversalVerifierContractMigrationHelper.ts +++ b/helpers/UniversalVerifierContractMigrationHelper.ts @@ -104,13 +104,9 @@ export class UniversalVerifierContractMigrationHelper extends ContractMigrationS } @log - async upgradeContract( - universalVerifierContract: Contract, - opts: { verifierLibAddress: string }, - ): Promise { + async upgradeContract(universalVerifierContract: Contract): Promise { return await this._universalVerifierDeployHelper.upgradeUniversalVerifier( await universalVerifierContract.getAddress(), - opts.verifierLibAddress, ); } } diff --git a/helpers/constants.ts b/helpers/constants.ts index 354845dc..43c4c4df 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.5", - unifiedAddress: "0xfcc86A79fCb057A8e55C6B853dff9479C3cf607c", - create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.UniversalVerifier")), + version: "2.0.0", + unifiedAddress: "0xfcc86A79fCb057A8e55C6B853dff9479C3cf607c", // TODO: Recalculate new address + 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: [], @@ -125,7 +125,7 @@ export const contractsInfo = Object.freeze({ }, STATE: { name: "State", - version: "2.6.1", + version: "2.6.2", unifiedAddress: "0x3C9acB2205Aa72A05F6D77d708b5Cf85FCa3a896", create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.State")), verificationOpts: { @@ -193,10 +193,26 @@ export const contractsInfo = Object.freeze({ libraries: {}, }, }, + VALIDATOR_LINKED_MULTI_QUERY: { + name: "LinkedMultiQueryValidator", + version: "1.0.0-beta.1", + unifiedAddress: "0xfA622418d7aBF33868545732CaD2C2E7ce9B16C8", + create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.LinkedMultiQueryValidator")), + verificationOpts: { + constructorArgsImplementation: [], + constructorArgsProxy: [ + "0x56fF81aBB5cdaC478bF236db717e4976b2ff841e", + "0xae15d2023a76174a940cbb2b7f44012c728b9d74", + "0x6964656e332e637265617465322e556e6976657273616c5665726966696572", + ], + constructorArgsProxyAdmin: ["0xAe15d2023A76174a940cbb2b7f44012c728b9d74"], + libraries: {}, + }, + }, VALIDATOR_AUTH_V2: { name: "AuthV2Validator", version: "1.0.0", - unifiedAddress: "0x49ebdC163fa014F310CeDBc8e4a0b15C738D8073", + unifiedAddress: "0x535F6a1B30533616CE4bD44081ea7A17CF2042B8", create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.AuthV2Validator")), verificationOpts: { constructorArgsImplementation: [], @@ -228,7 +244,7 @@ export const contractsInfo = Object.freeze({ VC_PAYMENT: { name: "VCPayment", version: "1.0.0", - unifiedAddress: "", + unifiedAddress: "0xba83D99c87358Ef9B6f7c4a5A94021A58d870704", create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.VCPayment")), verificationOpts: { constructorArgsImplementation: [], @@ -238,7 +254,7 @@ export const contractsInfo = Object.freeze({ MC_PAYMENT: { name: "MCPayment", version: "1.0.0", - unifiedAddress: "", + unifiedAddress: "0xe317A4f1450116b2fD381446DEaB41c882D6136D", create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.MCPayment")), verificationOpts: { constructorArgsImplementation: [], @@ -327,46 +343,40 @@ export const contractsInfo = Object.freeze({ libraries: {}, }, }, - GROTH16_VERIFIER_AUTH_V2: { - name: "Groth16VerifierAuthV2Wrapper", + GROTH16_VERIFIER_LINKED_MULTI_QUERY10: { + name: "Groth16VerifierLinkedMultiQuery10Wrapper", unifiedAddress: "", create2Calldata: "", verificationOpts: { contract: - "contracts/lib/groth16-verifiers/Groth16VerifierAuthV2Wrapper.sol:Groth16VerifierAuthV2Wrapper", - constructorArgsImplementation: [], - libraries: {}, - }, - }, - STATE_LIB: { - name: "StateLib", - unifiedAddress: "", - create2Address: "", - verificationOpts: { + "contracts/lib/groth16-verifiers/Groth16VerifierLinkedMultiQuery10Wrapper.sol:Groth16VerifierLinkedMultiQuery10Wrapper", constructorArgsImplementation: [], libraries: {}, }, }, - STATE_CROSS_CHAIN_LIB: { - name: "StateCrossChainLib", + GROTH16_VERIFIER_AUTH_V2: { + name: "Groth16VerifierAuthV2Wrapper", unifiedAddress: "", - create2Address: "", + create2Calldata: "", verificationOpts: { + contract: + "contracts/lib/groth16-verifiers/Groth16VerifierAuthV2Wrapper.sol:Groth16VerifierAuthV2Wrapper", constructorArgsImplementation: [], libraries: {}, }, }, - VERIFIER_LIB: { - name: "VerifierLib", + STATE_LIB: { + name: "StateLib", unifiedAddress: "", - create2Address: "", + create2Calldata: "", verificationOpts: { constructorArgsImplementation: [], libraries: {}, }, }, - EMBEDDED_ZKP_VERIFIER_WRAPPER: { - name: "EmbeddedZKPVerifierWrapper", + + EMBEDDED_VERIFIER_WRAPPER: { + name: "EmbeddedVerifierWrapper", unifiedAddress: "", create2Calldata: "", }, diff --git a/ignition/modules/libraries.ts b/ignition/modules/libraries.ts index e6225aac..ddb26aab 100644 --- a/ignition/modules/libraries.ts +++ b/ignition/modules/libraries.ts @@ -125,8 +125,3 @@ export const SpongePoseidonModule = buildModule("SpongePoseidonModule", (m) => { }); return { spongePoseidon }; }); - -export const VerifierLibModule = buildModule("VerifierLibModule", (m) => { - const verifierLib = m.contract("VerifierLib"); - return { verifierLib }; -}); diff --git a/ignition/modules/linkedMultiQuery.ts b/ignition/modules/linkedMultiQuery.ts new file mode 100644 index 00000000..f838f8a3 --- /dev/null +++ b/ignition/modules/linkedMultiQuery.ts @@ -0,0 +1,22 @@ +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; +import { contractsInfo } from "../../helpers/constants"; + +export const LinkedMultiQueryProxyModule = buildModule("LinkedMultiQueryModule", (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_LINKED_MULTI_QUERY.create2Calldata, + ]); + + const proxyAdminAddress = m.readEventArgument(proxy, "AdminChanged", "newAdmin"); + const proxyAdmin = m.contractAt("ProxyAdmin", proxyAdminAddress); + return { proxyAdmin, proxy }; +}); diff --git a/scripts/deploy/deployCrossChainVerifierWithRequests.ts b/scripts/deploy/deployCrossChainVerifierWithRequests.ts index 834a0030..0921d6bb 100644 --- a/scripts/deploy/deployCrossChainVerifierWithRequests.ts +++ b/scripts/deploy/deployCrossChainVerifierWithRequests.ts @@ -39,7 +39,7 @@ async function main() { console.log("Removing previous ignition files for chain: ", chainId); fs.rmSync(`./ignition/deployments/chain-${chainId}`, { recursive: true, force: true }); } - // ##################### State with StateCrossChainLib deploy ##################### + // ##################### State deploy ##################### const { state, crossChainProofValidator } = await deployHelper.deployStateWithLibraries(); @@ -60,15 +60,8 @@ async function main() { await state.getAddress(), ); - // ##################### VerifierLib deploy ##################### - const verifierLib = await deployHelper.deployVerifierLib(); - // ##################### Universal Verifier deploy ##################### - const verifier = await deployHelper.deployUniversalVerifier( - undefined, - await state.getAddress(), - await verifierLib.getAddress(), - ); + const verifier = await deployHelper.deployUniversalVerifier(undefined, await state.getAddress()); const addToWhiteList1 = await verifier.addValidatorToWhitelist(await validatorSig.getAddress()); await addToWhiteList1.wait(); diff --git a/scripts/deploy/deployState.ts b/scripts/deploy/deployState.ts index 7c71c34c..d8ad13ed 100644 --- a/scripts/deploy/deployState.ts +++ b/scripts/deploy/deployState.ts @@ -40,22 +40,13 @@ async function main() { // if the state contract already exists we won't have new contracts deployed // to verify and to save the output - if ( - groth16VerifierStateTransition && - stateLib && - stateCrossChainLib && - crossChainProofValidator - ) { + if (groth16VerifierStateTransition && stateLib && crossChainProofValidator) { await verifyContract(await state.getAddress(), contractsInfo.STATE.verificationOpts); await verifyContract( await groth16VerifierStateTransition.getAddress(), contractsInfo.GROTH16_VERIFIER_STATE_TRANSITION.verificationOpts, ); await verifyContract(await stateLib.getAddress(), contractsInfo.STATE_LIB.verificationOpts); - await verifyContract( - await stateCrossChainLib.getAddress(), - contractsInfo.STATE_CROSS_CHAIN_LIB.verificationOpts, - ); await verifyContract( await crossChainProofValidator.getAddress(), contractsInfo.CROSS_CHAIN_PROOF_VALIDATOR.verificationOpts, @@ -69,7 +60,6 @@ async function main() { proxyAdminOwnerAddress: await signer.getAddress(), state: await state.getAddress(), stateLib: await stateLib?.getAddress(), - stateCrossChainLib: await stateCrossChainLib?.getAddress(), crossChainProofValidator: await crossChainProofValidator?.getAddress(), network: networkName, chainId, diff --git a/scripts/deploy/deployUniversalVerifier.ts b/scripts/deploy/deployUniversalVerifier.ts index 1e1188b8..391d2754 100644 --- a/scripts/deploy/deployUniversalVerifier.ts +++ b/scripts/deploy/deployUniversalVerifier.ts @@ -28,29 +28,9 @@ async function main() { "./scripts/deployments_output/temp_deployments_output.json", ); - let verifierLib = await tmpContractDeployments.getContract(contractsInfo.VERIFIER_LIB.name); - if (verifierLib) { - Logger.warning( - `${contractsInfo.VERIFIER_LIB.name} found already deployed to: ${await verifierLib?.getAddress()}`, - ); - } else { - verifierLib = await deployHelper.deployVerifierLib(); - const tx = await verifierLib.deploymentTransaction(); - await waitNotToInterfereWithHardhatIgnition(tx); - tmpContractDeployments.addContract( - contractsInfo.VERIFIER_LIB.name, - await verifierLib.getAddress(), - ); - await verifyContract( - await verifierLib.getAddress(), - contractsInfo.VERIFIER_LIB.verificationOpts, - ); - } - const universalVerifier = await deployHelper.deployUniversalVerifier( undefined, stateContractAddress, - await verifierLib.getAddress(), deployStrategy, ); tmpContractDeployments.remove(); @@ -68,7 +48,6 @@ async function main() { const outputJson = { proxyAdminOwnerAddress: await signer.getAddress(), universalVerifier: await universalVerifier.getAddress(), - verifierLib: await verifierLib.getAddress(), state: stateContractAddress, network: networkName, chainId, diff --git a/scripts/deploy/deployValidators.ts b/scripts/deploy/deployValidators.ts index b3f5b3c1..6e23fd40 100644 --- a/scripts/deploy/deployValidators.ts +++ b/scripts/deploy/deployValidators.ts @@ -13,14 +13,14 @@ async function main() { const config = getConfig(); const chainId = await getChainId(); - const stateContractAddress = await getStateContractAddress(); - const validators: ValidatorType[] = ["mtpV2", "sigV2", "v3", "authV2"]; + const validators: ValidatorType[] = ["mtpV2", "sigV2", "v3", "lmk"]; const deployStrategy: "basic" | "create2" = config.deployStrategy == "create2" ? "create2" : "basic"; const [signer] = await hre.ethers.getSigners(); const deployHelper = await DeployHelper.initialize(null, true); + const stateContractAddress = getStateContractAddress(); const validatorsInfo: any = []; for (const v of validators) { diff --git a/scripts/maintenance/checkContractsVerificationManually.ts b/scripts/maintenance/checkContractsVerificationManually.ts index 6a35c530..04a218df 100644 --- a/scripts/maintenance/checkContractsVerificationManually.ts +++ b/scripts/maintenance/checkContractsVerificationManually.ts @@ -2,29 +2,16 @@ import { verifyContract } from "../../helpers/helperUtils"; import { contractsInfo } from "../../helpers/constants"; async function main() { - const StateCrossChainLibAddress: string = ""; const StateLibAddress: string = ""; - const VerifierLibAddress: string = ""; const StateAddress: string = ""; if (StateAddress.includes("0x")) { await verifyContract(StateAddress, contractsInfo.STATE.verificationOpts); } - if (StateCrossChainLibAddress.includes("0x")) { - await verifyContract( - StateCrossChainLibAddress, - contractsInfo.STATE_CROSS_CHAIN_LIB.verificationOpts, - ); - } - if (StateLibAddress.includes("0x")) { await verifyContract(StateLibAddress, contractsInfo.STATE_LIB.verificationOpts); } - - if (VerifierLibAddress.includes("0x")) { - await verifyContract(VerifierLibAddress, contractsInfo.VERIFIER_LIB.verificationOpts); - } } main() diff --git a/scripts/maintenance/checkEmbeddedZKPVerifier.ts b/scripts/maintenance/checkEmbeddedZKPVerifier.ts index e919726c..e69de29b 100644 --- a/scripts/maintenance/checkEmbeddedZKPVerifier.ts +++ b/scripts/maintenance/checkEmbeddedZKPVerifier.ts @@ -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: await 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: await 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: await 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/scripts/maintenance/disableProxyContract.ts b/scripts/maintenance/disableProxyContract.ts index 3a271e49..a04a0bd5 100644 --- a/scripts/maintenance/disableProxyContract.ts +++ b/scripts/maintenance/disableProxyContract.ts @@ -21,7 +21,10 @@ async function main() { await new Promise((resolve) => setTimeout(resolve, 20000)); // !!!!! Put proper function name here to make some check, e.g. getDefaultIdType() for State contract !!!!! - await expect(contract.getDefaultIdType()).to.be.revertedWith("The contract is disabled"); + await expect(contract.getDefaultIdType()).to.be.revertedWithCustomError( + contract, + "TheContractIsDisabled", + ); const network = hre.network.name; console.log( diff --git a/scripts/upgrade/state/state-upgrade.ts b/scripts/upgrade/state/state-upgrade.ts index c9bdb7e5..b812c51e 100644 --- a/scripts/upgrade/state/state-upgrade.ts +++ b/scripts/upgrade/state/state-upgrade.ts @@ -79,22 +79,17 @@ async function main() { const defaultIdTypeBefore = await stateContract.getDefaultIdType(); const stateOwnerAddressBefore = await stateContract.owner(); - const { state, stateLib, stateCrossChainLib, crossChainProofValidator } = - await stateDeployHelper.upgradeState( - await stateContract.getAddress(), - true, - contractsInfo.SMT_LIB.unifiedAddress, - contractsInfo.POSEIDON_1.unifiedAddress, - ); + const { state, stateLib, crossChainProofValidator } = await stateDeployHelper.upgradeState( + await stateContract.getAddress(), + true, + contractsInfo.SMT_LIB.unifiedAddress, + contractsInfo.POSEIDON_1.unifiedAddress, + ); console.log("Version after: ", await state.VERSION()); await verifyContract(await state.getAddress(), contractsInfo.STATE.verificationOpts); await verifyContract(await stateLib.getAddress(), contractsInfo.STATE_LIB.verificationOpts); - await verifyContract( - await stateCrossChainLib.getAddress(), - contractsInfo.STATE_CROSS_CHAIN_LIB.verificationOpts, - ); await verifyContract( await crossChainProofValidator.getAddress(), contractsInfo.CROSS_CHAIN_PROOF_VALIDATOR.verificationOpts, @@ -149,7 +144,6 @@ async function main() { verifier: contractsInfo.GROTH16_VERIFIER_STATE_TRANSITION.unifiedAddress, stateLib: await stateLib.getAddress(), smtLib: contractsInfo.SMT_LIB.unifiedAddress, - stateCrossChainLib: await stateCrossChainLib.getAddress(), crossChainProofValidator: await crossChainProofValidator.getAddress(), poseidon1: contractsInfo.POSEIDON_1.unifiedAddress, poseidon2: contractsInfo.POSEIDON_2.unifiedAddress, diff --git a/scripts/upgrade/verifiers/embedded-verifier-upgrade.ts b/scripts/upgrade/verifiers/embedded-verifier-upgrade.ts index f3009b21..ef7fb600 100644 --- a/scripts/upgrade/verifiers/embedded-verifier-upgrade.ts +++ b/scripts/upgrade/verifiers/embedded-verifier-upgrade.ts @@ -17,11 +17,11 @@ async function main() { const chainId = await getChainId(); const network = hre.network.name; - // 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 "", ); @@ -42,14 +42,10 @@ async function main() { const verifierRequestsCountBeforeUpgrade = await verifierContract.getZKPRequestsCount(); console.log("Owner Address Before Upgrade: ", verifierOwnerAddressBeforeUpgrade); - 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, + ); try { verifierContract = await upgrades.upgradeProxy( @@ -98,7 +94,6 @@ async function main() { const outputJson = { proxyAdminOwnerAddress: await signer.getAddress(), verifierContract: await verifierContract.getAddress(), - verifierLib: await verifierLib.getAddress(), state: stateContractAddress, network: network, chainId, diff --git a/scripts/upgrade/verifiers/universal-verifier-upgrade.ts b/scripts/upgrade/verifiers/universal-verifier-upgrade.ts index ed73ab80..000f2df1 100644 --- a/scripts/upgrade/verifiers/universal-verifier-upgrade.ts +++ b/scripts/upgrade/verifiers/universal-verifier-upgrade.ts @@ -123,16 +123,8 @@ async function main() { expect(await universalVerifierContract.isWhitelistedValidator(validator)).to.equal(true); } - const verifierLib = await deployerHelper.deployVerifierLib(); - const txVerifLib = await verifierLib.deploymentTransaction(); - await waitNotToInterfereWithHardhatIgnition(txVerifLib); - - await verifyContract(await verifierLib.getAddress(), contractsInfo.VERIFIER_LIB.verificationOpts); - // **** Upgrade Universal Verifier **** - await universalVerifierMigrationHelper.upgradeContract(universalVerifierContract, { - verifierLibAddress: await verifierLib.getAddress(), - }); + await universalVerifierMigrationHelper.upgradeContract(universalVerifierContract); // ************************ console.log("Checking data after upgrade"); @@ -217,7 +209,6 @@ async function main() { const outputJson = { proxyAdminOwnerAddress: await proxyAdminOwnerSigner.getAddress(), universalVerifier: await universalVerifierContract.getAddress(), - verifierLib: await verifierLib.getAddress(), state: stateContractAddress, network: network, chainId, diff --git a/test/IdentityTreeStore/IdentityTreeStore.test.ts b/test/IdentityTreeStore/IdentityTreeStore.test.ts index 230db1a0..f6096d66 100644 --- a/test/IdentityTreeStore/IdentityTreeStore.test.ts +++ b/test/IdentityTreeStore/IdentityTreeStore.test.ts @@ -295,10 +295,11 @@ describe("IdentityTreeStore", function () { await expect( identityTreeStore.getRevocationStatusByIdAndState(id, state, nonce), - ).to.be.rejectedWith("Invalid state node"); + ).to.be.revertedWithCustomError(identityTreeStore, "InvalidStateNode"); - await expect(identityTreeStore.getRevocationStatus(id, nonce)).to.be.rejectedWith( - "Invalid state node", + await expect(identityTreeStore.getRevocationStatus(id, nonce)).to.be.revertedWithCustomError( + identityTreeStore, + "InvalidStateNode", ); }); }); diff --git a/test/check-unified-addresses.test.ts b/test/check-unified-addresses.test.ts new file mode 100644 index 00000000..75b9f833 --- /dev/null +++ b/test/check-unified-addresses.test.ts @@ -0,0 +1,72 @@ +import { buildModule } from "@nomicfoundation/ignition-core"; +import { contractsInfo } from "../helpers/constants"; +import hre, { ethers, ignition } from "hardhat"; +import { Logger } from "../helpers/helperUtils"; + +// Replace here with your own proxy admin owner address +const proxyAdminOwnerAddress = "0xAe15d2023A76174a940cbb2b7F44012C728B9d74"; + +const Create2AddressAnchorModule = buildModule("Create2AddressAnchorModule", (m) => { + const create2AddressAnchor = m.contract(contractsInfo.CREATE2_ADDRESS_ANCHOR.name, { + abi: [], + contractName: contractsInfo.CREATE2_ADDRESS_ANCHOR.name, + bytecode: "0x6005600C60003960056000F360006000F3", + sourceName: "", + linkReferences: {}, + }); + + return { create2AddressAnchor }; +}); + +const GeneralProxyModule = buildModule("GeneralProxyModule", (m) => { + const create2Calldata = m.getParameter("create2Calldata", 0); + + const proxy = m.contract("TransparentUpgradeableProxy", [ + contractsInfo.CREATE2_ADDRESS_ANCHOR.unifiedAddress, + proxyAdminOwnerAddress, + create2Calldata, + ]); + const proxyAdminAddress = m.readEventArgument(proxy, "AdminChanged", "newAdmin"); + const proxyAdmin = m.contractAt("ProxyAdmin", proxyAdminAddress); + + return { proxyAdmin, proxy }; +}); + +it("Calculate and check unified addresses for proxy contracts", async () => { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [proxyAdminOwnerAddress], + }); + + (await ethers.getSigners())[0].sendTransaction({ + to: proxyAdminOwnerAddress, + value: ethers.parseEther("1.0"), // Sends exactly 1.0 ether + }); + + await ignition.deploy(Create2AddressAnchorModule, { strategy: "create2" }); + + for (const property in contractsInfo) { + if (contractsInfo[property].create2Calldata !== "") { + const proxyDeployed = ( + await ignition.deploy(GeneralProxyModule, { + strategy: "create2", + parameters: { + GeneralProxyModule: { + create2Calldata: contractsInfo[property].create2Calldata, + }, + }, + }) + ).proxy; + await proxyDeployed.waitForDeployment(); + if ((await proxyDeployed.getAddress()) !== contractsInfo[property].unifiedAddress) { + Logger.error( + `${contractsInfo[property].name} deployed with unified address: ${await proxyDeployed.getAddress()} (expected: ${contractsInfo[property].unifiedAddress})`, + ); + } else { + Logger.success( + `${contractsInfo[property].name} deployed with unified address: ${await proxyDeployed.getAddress()}`, + ); + } + } + } +}); diff --git a/test/cross-chain/cross-chain-proof-validator.test.ts b/test/cross-chain/cross-chain-proof-validator.test.ts index 7f66f0ba..9940ecec 100644 --- a/test/cross-chain/cross-chain-proof-validator.test.ts +++ b/test/cross-chain/cross-chain-proof-validator.test.ts @@ -91,9 +91,9 @@ describe("State Cross Chain", function () { }; const proof = await packIdentityStateUpdateWithSignature(ism, signer); - await expect(crossChainProofValidator.processIdentityStateProof(proof)).to.be.rejectedWith( - "Oracle timestamp cannot be in the past", - ); + await expect( + crossChainProofValidator.processIdentityStateProof(proof), + ).to.be.revertedWithCustomError(crossChainProofValidator, "OracleTimestampCannotBeInThePast"); }); it("Oracle replacedAtTimestamp or oracle timestamp cannot be in the future", async function () { @@ -107,8 +107,11 @@ describe("State Cross Chain", function () { }; let proof = await packGlobalStateUpdateWithSignature(gsm, signer); - await expect(crossChainProofValidator.processGlobalStateProof(proof)).to.be.rejectedWith( - "Oracle replacedAtTimestamp or oracle timestamp cannot be in the future", + await expect( + crossChainProofValidator.processGlobalStateProof(proof), + ).to.be.revertedWithCustomError( + crossChainProofValidator, + "OracleReplacedAtTimestampCannotBeInTheFuture", ); const ism: IdentityStateMessage = { @@ -119,8 +122,11 @@ describe("State Cross Chain", function () { }; proof = await packIdentityStateUpdateWithSignature(ism, signer); - await expect(crossChainProofValidator.processIdentityStateProof(proof)).to.be.rejectedWith( - "Oracle replacedAtTimestamp or oracle timestamp cannot be in the future", + await expect( + crossChainProofValidator.processIdentityStateProof(proof), + ).to.be.revertedWithCustomError( + crossChainProofValidator, + "OracleReplacedAtTimestampCannotBeInTheFuture", ); }); @@ -135,8 +141,11 @@ describe("State Cross Chain", function () { }; let proof = await packGlobalStateUpdateWithSignature(gsm, signer, true); - await expect(crossChainProofValidator.processGlobalStateProof(proof)).to.be.rejectedWith( - "Global state proof signing address is not valid", + await expect( + crossChainProofValidator.processGlobalStateProof(proof), + ).to.be.revertedWithCustomError( + crossChainProofValidator, + "GlobalStateProofSigningAddressInvalid", ); const ism: IdentityStateMessage = { @@ -147,8 +156,11 @@ describe("State Cross Chain", function () { }; proof = await packIdentityStateUpdateWithSignature(ism, signer, true); - await expect(crossChainProofValidator.processIdentityStateProof(proof)).to.be.rejectedWith( - "Identity state proof signing address is not valid", + await expect( + crossChainProofValidator.processIdentityStateProof(proof), + ).to.be.revertedWithCustomError( + crossChainProofValidator, + "IdentityStateProofSigningAddressInvalid", ); }); @@ -163,9 +175,9 @@ describe("State Cross Chain", function () { }; let proof = await packGlobalStateUpdateWithSignature(gsm, signer, false, true); - await expect(crossChainProofValidator.processGlobalStateProof(proof)).to.be.rejectedWith( - "Global state proof is not valid", - ); + await expect( + crossChainProofValidator.processGlobalStateProof(proof), + ).to.be.revertedWithCustomError(crossChainProofValidator, "GlobalStateProofInvalid"); const ism: IdentityStateMessage = { timestamp: currentTimestamp, @@ -175,8 +187,8 @@ describe("State Cross Chain", function () { }; proof = await packIdentityStateUpdateWithSignature(ism, signer, false, true); - await expect(crossChainProofValidator.processIdentityStateProof(proof)).to.be.rejectedWith( - "Identity state proof is not valid", - ); + await expect( + crossChainProofValidator.processIdentityStateProof(proof), + ).to.be.revertedWithCustomError(crossChainProofValidator, "IdentityStateProofInvalid"); }); }); diff --git a/test/disable-proxy.test.ts b/test/disable-proxy.test.ts index 37a7edb5..ae87ea7d 100644 --- a/test/disable-proxy.test.ts +++ b/test/disable-proxy.test.ts @@ -20,8 +20,8 @@ describe("Disable Proxy Contract test", async () => { const alwaysRevertFactory = await ethers.getContractFactory("AlwaysRevert"); await upgrades.upgradeProxy(await verifier.getAddress(), alwaysRevertFactory); - await expect(verifier.verifyProof(d[0], d[1], d[2], d[3])).to.be.revertedWith( - "The contract is disabled", + await expect(verifier.verifyProof(d[0], d[1], d[2], d[3])).to.be.rejectedWith( + "TheContractIsDisabled()", ); await upgrades.upgradeProxy(await verifier.getAddress(), verifierStubFactory); diff --git a/test/integration-tests/data/user_claim_issued_on_userid_v3.json b/test/integration-tests/data/user_claim_issued_on_userid_v3.json new file mode 100644 index 00000000..1752523d --- /dev/null +++ b/test/integration-tests/data/user_claim_issued_on_userid_v3.json @@ -0,0 +1,43 @@ +{ + "pub_signals": [ + "23273167900576580892722615617815475823351560716009055944677723144398443009", + "5163501582380794606957519356304223666405959765481558471633650858228490409290", + "2943483356559152311923412925436024635269538717812859789851139200242297094", + "20336008450539684768013573494073798243349685857640613070314041678185349736439", + "0", + "0", + "1", + "41", + "583091486781463398742321306787801699791102451699", + "0", + "22057981499787921734624217749308316644136637822444794206796063681866502657", + "2943483356559152311923412925436024635269538717812859789851139200242297094", + "1642074362", + "1" + ], + "proof": { + "pi_a": [ + "19669163552065605232226440325758625657636624385583277995594891950486344866508", + "10711637643378840554760385822550851249555527485204302585948254974040755477609", + "1" + ], + "pi_b": [ + [ + "2876377569478858706516978096403208510392181149089058781420525859804877516279", + "9515133907790273150201355935211769526767866212205498667361695701553682353137" + ], + [ + "14869390077573764384106112054362589649141652233556283316748964628563711405904", + "1151936578002145346443589143139936031757296603439509995084143527963932212544" + ], + ["1", "0"] + ], + "pi_c": [ + "18589419753357207419833045626873608018236514226418635490877679898608141092303", + "4377014052512563002949775596611087745477480197663974829547855257127755207594", + "1" + ], + "protocol": "groth16", + "curve": "bn128" + } +} diff --git a/test/integration-tests/data/user_genesis_auth.json b/test/integration-tests/data/user_genesis_auth.json new file mode 100644 index 00000000..68afce0f --- /dev/null +++ b/test/integration-tests/data/user_genesis_auth.json @@ -0,0 +1,32 @@ +{ + "pub_signals": [ + "23273167900576580892722615617815475823351560716009055944677723144398443009", + "5212973139745638668633720237501954966656555739014896868936311397139229290378", + "0" + ], + "proof": { + "pi_a": [ + "1238064536595227341390695164336084541847939190316067358993916901940214723234", + "15799255647621631452959766830308132916683864863600158271743280920118665788588", + "1" + ], + "pi_b": [ + [ + "17670400722045843961660874132388709522679013234267198690084857080641983583843", + "5287730570087617132913682200031155642104951958077715792787827776465000719881" + ], + [ + "6075263630519803963226139789246602077914731950499703659787166643095603330195", + "7938053062178962435112746593441459158641110827412215757397151271877979765565" + ], + ["1", "0"] + ], + "pi_c": [ + "21010592776578764547907351859929622777512296832984420047235514552228625464856", + "18192765798069122918377413910327891618012999130269311083464781452775699686570", + "1" + ], + "protocol": "groth16", + "curve": "bn128" + } +} diff --git a/test/integration-tests/data/user_genesis_auth_challenge_invalid.json b/test/integration-tests/data/user_genesis_auth_challenge_invalid.json new file mode 100644 index 00000000..890bb82c --- /dev/null +++ b/test/integration-tests/data/user_genesis_auth_challenge_invalid.json @@ -0,0 +1,32 @@ +{ + "pub_signals": [ + "23273167900576580892722615617815475823351560716009055944677723144398443009", + "2378043419791432977129888049610162411300945363341716024701363947598560258096", + "0" + ], + "proof": { + "pi_a": [ + "9541867327348938145541193982123992297208138293358805560483945427733735306124", + "13833819619741404300235158867440476234063725794567625680704749737959767863116", + "1" + ], + "pi_b": [ + [ + "20359745551134549335223867766905772056177316525265757287933840985984292914937", + "1780864031735933858234307839702997430255911747389589653941781093680324195492" + ], + [ + "12080937648102594742202930954953883943640192768810066345568670629167309979739", + "13280726496624428913315243268401447342745276267258692974299666120924393078428" + ], + ["1", "0"] + ], + "pi_c": [ + "12131738695386235649587882341686961353232752515854843634252155993701923991681", + "18949649035566368476284982950769667836111086011759734792131512893325609072891", + "1" + ], + "protocol": "groth16", + "curve": "bn128" + } +} diff --git a/test/integration-tests/data/user_linked_multi_query.json b/test/integration-tests/data/user_linked_multi_query.json new file mode 100644 index 00000000..9bb0c3d1 --- /dev/null +++ b/test/integration-tests/data/user_linked_multi_query.json @@ -0,0 +1,51 @@ +{ + "pub_signals": [ + "20336008450539684768013573494073798243349685857640613070314041678185349736439", + "1", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "3326382892536126749483088946048689911243394580824744244053752370464747528203", + "9907132056133666096701539062450765284880813426582692863734448403438789333698", + "13362042977965885903820557513534065802896288300017199700677633721405805677442", + "13362042977965885903820557513534065802896288300017199700677633721405805677442", + "13362042977965885903820557513534065802896288300017199700677633721405805677442", + "13362042977965885903820557513534065802896288300017199700677633721405805677442", + "13362042977965885903820557513534065802896288300017199700677633721405805677442", + "13362042977965885903820557513534065802896288300017199700677633721405805677442", + "13362042977965885903820557513534065802896288300017199700677633721405805677442", + "13362042977965885903820557513534065802896288300017199700677633721405805677442" + ], + "proof": { + "pi_a": [ + "10541909102768476058347747538132221338909944796214430823937430621918433820572", + "12998271972533338175505729586393778918022420191938730276405884463074788961369", + "1" + ], + "pi_b": [ + [ + "21786734761872104002981387083471529709973058267263166350818620656227477577301", + "6784160731236418180364992784613101482914038854158831514386379008042983363743" + ], + [ + "17941889499361474971416705471447893349857158046615762117348229875623276791888", + "5989944269623828035204132704907772907469171386995335116029892218132563943912" + ], + ["1", "0"] + ], + "pi_c": [ + "17192623970124500187048482626581622763069786710232775241298788913349580516363", + "17848067197475549139335038191779648120820902475170770311958006870826316291138", + "1" + ], + "protocol": "groth16", + "curve": "bn128" + } +} diff --git a/test/integration-tests/integration-verifier.test.ts b/test/integration-tests/integration-verifier.test.ts new file mode 100644 index 00000000..8b44478b --- /dev/null +++ b/test/integration-tests/integration-verifier.test.ts @@ -0,0 +1,259 @@ +import { ethers } from "hardhat"; +import { DeployHelper } from "../../helpers/DeployHelper"; +import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; +import { prepareInputs } from "../utils/state-utils"; +import authProofJson from "./data/user_genesis_auth.json"; +import authInvalidChallengeProofJson from "./data/user_genesis_auth_challenge_invalid.json"; +import v3ProofJson from "./data/user_claim_issued_on_userid_v3.json"; +import linkedProofJson from "./data/user_linked_multi_query.json"; +import { packZKProof } from "../utils/packData"; +import { expect } from "chai"; +import { + packLinkedMultiQueryValidatorParams, + packV3ValidatorParams, +} from "../utils/validator-pack-utils"; +import { CircuitId } from "@0xpolygonid/js-sdk"; +import { calculateQueryHashV3 } from "../utils/query-hash-utils"; +import { TEN_YEARS } from "../../helpers/constants"; + +describe("Verifier Integration test", function () { + let verifier, authValidator, v3Validator, lmkValidator; + let signer; + + const requestIdV3 = 32; + const requestIdLMK = 33; + const groupID = 1; + + const value = ["20020101", ...new Array(63).fill("0")]; + + const schema = "267831521922558027206082390043321796944"; + const slotIndex = 0; // 0 for signature + const operator = 7; + 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 query = { + schema, + claimPathKey, + operator, + slotIndex, + value, + circuitIds: [CircuitId.AtomicQueryV3OnChain], + skipClaimRevocationCheck: false, + queryHash, + groupID: groupID, + nullifierSessionID: nullifierSessionId, // for ethereum based user + proofType: 1, // 1 for BJJ + verifierID: verifierId, + }; + + const crossChainProofs = "0x"; + const metadatas = "0x"; + const authMethod = "authV2"; + + const v3Params = packV3ValidatorParams(query); + + const twoQueries = { + claimPathKey: [ + 20376033832371109177683048456014525905119173674985843915445634726167450989630n, + 20376033832371109177683048456014525905119173674985843915445634726167450989630n, + ], + operator: [2, 6], + slotIndex: [0, 0], + value: [ + [20020101, ...new Array(63).fill(0)], + [20030101, ...new Array(63).fill(0)], + ], + queryHash: [ + 3326382892536126749483088946048689911243394580824744244053752370464747528203n, + 9907132056133666096701539062450765284880813426582692863734448403438789333698n, + ], + circuitIds: ["linkedMultiQuery10-beta.1"], + groupID: groupID, + verifierID: verifierId, + }; + + const twoQueriesParams = packLinkedMultiQueryValidatorParams(twoQueries); + + const v3Proof = getProof(v3ProofJson); + const lmqProof = getProof(linkedProofJson); + + function getProof(proofJson: any) { + const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + return proof; + } + + async function deployContractsFixture() { + [signer] = await ethers.getSigners(); + + const verifier = await ethers.deployContract("VerifierTestWrapper", []); + + const deployHelper = await DeployHelper.initialize(null, true); + const { state } = await deployHelper.deployStateWithLibraries(["0x0212"]); + await verifier.initialize(await state.getAddress()); + + const { validator: authValidator } = await deployHelper.deployValidatorContractsWithVerifiers( + "authV2", + await state.getAddress(), + ); + await authValidator.setProofExpirationTimeout(TEN_YEARS); + await authValidator.setGISTRootExpirationTimeout(TEN_YEARS); + + const authMethod = { + authMethod: "authV2", + validator: await authValidator.getAddress(), + params: "0x", + }; + await verifier.setAuthMethod(authMethod); + + const { validator: v3Validator } = await deployHelper.deployValidatorContractsWithVerifiers( + "v3", + await state.getAddress(), + ); + await v3Validator.setProofExpirationTimeout(TEN_YEARS); + await v3Validator.setGISTRootExpirationTimeout(TEN_YEARS); + + const { validator: lmkValidator } = await deployHelper.deployValidatorContractsWithVerifiers( + "lmk", + await state.getAddress(), + ); + + return { state, verifier, authValidator, v3Validator, lmkValidator }; + } + + beforeEach(async () => { + ({ verifier, authValidator, v3Validator, lmkValidator } = + await loadFixture(deployContractsFixture)); + + await verifier.setVerifierID(query.verifierID); + }); + + it("Should revert with ChallengeIsInvalid for auth proof", async function () { + const authInvalidChallengeProof = getProof(authInvalidChallengeProofJson); + + await verifier.setRequests([ + { + requestId: requestIdV3, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: v3Params, + }, + { + requestId: requestIdLMK, + metadata: "metadata", + validator: await lmkValidator.getAddress(), + params: twoQueriesParams, + }, + ]); + + await expect( + verifier.submitResponse( + { + authMethod: authMethod, + proof: authInvalidChallengeProof, + }, + [ + { + requestId: requestIdV3, + proof: v3Proof, + metadata: metadatas, + }, + { + requestId: requestIdLMK, + proof: lmqProof, + metadata: metadatas, + }, + ], + crossChainProofs, + ), + ).to.be.revertedWithCustomError(verifier, "ChallengeIsInvalid"); + }); + + it("Should verify", async function () { + const authProof = getProof(authProofJson); + + // 1. Create the requests + await verifier.setRequests([ + { + requestId: requestIdV3, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: v3Params, + }, + { + requestId: requestIdLMK, + metadata: "metadata", + validator: await lmkValidator.getAddress(), + params: twoQueriesParams, + }, + ]); + + const multiRequest = { + multiRequestId: 1, + requestIds: [], + groupIds: [groupID], + metadata: "0x", + }; + + // 2. Create the multi-request + await expect(verifier.setMultiRequest(multiRequest)).not.to.be.reverted; + const multiRequestIdExists = await verifier.multiRequestIdExists(multiRequest.multiRequestId); + expect(multiRequestIdExists).to.be.true; + + let areMultiRequestProofsVerified = await verifier.areMultiRequestProofsVerified( + multiRequest.multiRequestId, + await signer.getAddress(), + ); + expect(areMultiRequestProofsVerified).to.be.false; + + // 3. Submitting a response with valid proofs + await expect( + verifier.submitResponse( + { + authMethod: authMethod, + proof: authProof, + }, + [ + { + requestId: requestIdV3, + proof: v3Proof, + metadata: metadatas, + }, + { + requestId: requestIdLMK, + proof: lmqProof, + metadata: metadatas, + }, + ], + crossChainProofs, + ), + ).not.to.be.reverted; + + areMultiRequestProofsVerified = await verifier.areMultiRequestProofsVerified( + multiRequest.multiRequestId, + await signer.getAddress(), + ); + expect(areMultiRequestProofsVerified).to.be.true; + }); + + // An integration test with a MultiRequest + // The multiRequest has a single group with two requests inside + // One request is based on V3 validator + // Another one is based on LinkedMultiQuery validator +}); diff --git a/test/onchain-identity/claim-builder.test.ts b/test/onchain-identity/claim-builder.test.ts index 374c9fd5..dde6c256 100644 --- a/test/onchain-identity/claim-builder.test.ts +++ b/test/onchain-identity/claim-builder.test.ts @@ -2,7 +2,7 @@ import { expect } from "chai"; import { OnchainIdentityDeployHelper } from "../../helpers/OnchainIdentityDeployHelper"; import fs from "fs"; -describe("Claim builder tests", function() { +describe("Claim builder tests", function () { let identity; before(async () => { @@ -12,118 +12,512 @@ describe("Claim builder tests", function() { it("validate buildClaim", async function () { const inputs = [ - { // schemaHash - contractInput: ['75118319212313495155413841331241344325', 0, false, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - expectedClaims: ['75118319212313495155413841331241344325', '0', '0', '0', '0', '0', '0', '0'] - }, - { // id index - contractInput: ['75118319212313495155413841331241344325', 1, false, false, 0, 0, '25425363284463910957419549722021124450832239517990785975889689633068548096', 0, 0, 0, 0, 0, 0, 0], - expectedClaims: ['755683053054190422082163056194777767237', '25425363284463910957419549722021124450832239517990785975889689633068548096', '0', '0', '0', '0', '0', '0'] - }, - { // id value - contractInput: ['75118319212313495155413841331241344325', 2, false, false, 0, 0, '25425363284463910957419549722021124450832239517990785975889689633068548096', 0, 0, 0, 0, 0, 0, 0], - expectedClaims: ['1095965419975128885545537663626545978693', '0', '0', '0', '0', '25425363284463910957419549722021124450832239517990785975889689633068548096', '0', '0'] - }, - { // expirationDate - contractInput: ['75118319212313495155413841331241344325', 2, true, false, 0, 0, '25425363284463910957419549722021124450832239517990785975889689633068548096', 0, 1857686340, 0, 0, 0, 0, 0], - expectedClaims: ['3818224355342636593252534523080691670341', '0', '0', '0', '34268264483206187164568125440', '25425363284463910957419549722021124450832239517990785975889689633068548096', '0', '0'] - }, - { // updatableFlag - contractInput: ['75118319212313495155413841331241344325', 2, true, true, 0, 0, '25425363284463910957419549722021124450832239517990785975889689633068548096', 0, 1857686340, 0, 0, 0, 0, 0], - expectedClaims: ['9262742226077652008666528241988983053637', '0', '0', '0', '34268264483206187164568125440', '25425363284463910957419549722021124450832239517990785975889689633068548096', '0', '0'] - }, - { // merklized index - contractInput: ['75118319212313495155413841331241344325', 2, true, true, 1, 0, '25425363284463910957419549722021124450832239517990785975889689633068548096', 0, 1857686340, '93352129123234552352342342353456456452342343456345234121567843345', 0, 0, 0, 0], - expectedClaims: ['20151777967547682839494515679805565820229', '0', '93352129123234552352342342353456456452342343456345234121567843345', '0', '34268264483206187164568125440', '25425363284463910957419549722021124450832239517990785975889689633068548096', '0', '0'] - }, - { // merklized value - contractInput: ['75118319212313495155413841331241344325', 2, true, true, 2, 0, '25425363284463910957419549722021124450832239517990785975889689633068548096', 0, 1857686340, '93352129123234552352342342353456456452342343456345234121567843345', 0, 0, 0, 0], - expectedClaims: ['31040813709017713670322503117622148586821', '0', '0', '0', '34268264483206187164568125440', '25425363284463910957419549722021124450832239517990785975889689633068548096', '93352129123234552352342342353456456452342343456345234121567843345', '0'] - }, - { // version - contractInput: ['75118319212313495155413841331241344325', 2, true, true, 2, 89220123, '25425363284463910957419549722021124450832239517990785975889689633068548096', 0, 1857686340, '93352129123234552352342342353456456452342343456345234121567843345', 0, 0, 0, 0], - expectedClaims: ['130395355847364581104005408845894865439016836786170092869', '0', '0', '0', '34268264483206187164568125440', '25425363284463910957419549722021124450832239517990785975889689633068548096', '93352129123234552352342342353456456452342343456345234121567843345', '0'] - }, - { // revocation nonce - contractInput: ['75118319212313495155413841331241344325', 2, true, true, 2, 89220123, '25425363284463910957419549722021124450832239517990785975889689633068548096', 3312445, 1857686340, '93352129123234552352342342353456456452342343456345234121567843345', 0, 0, 0, 0], - expectedClaims: ['130395355847364581104005408845894865439016836786170092869', '0', '0', '0', '34268264483206187164571437885', '25425363284463910957419549722021124450832239517990785975889689633068548096', '93352129123234552352342342353456456452342343456345234121567843345', '0'] - }, - { // data slots - contractInput: ['75118319212313495155413841331241344325', 2, true, true, 0, 89220123, '25425363284463910957419549722021124450832239517990785975889689633068548096', 3312445, 1857686340, '0', '16243864111864693853212588481963275789994876191154110553066821559749894481761', '7078462697308959301666117070269719819629678436794910510259518359026273676830', '12448278679517811784508557734102986855579744384337338465055621486538311281772', '9260608685281348956030279125705000716237952776955782848598673606545494194823'], - expectedClaims: ['130395355847364559325933925905833203783041961153004559685', '0', '16243864111864693853212588481963275789994876191154110553066821559749894481761', '7078462697308959301666117070269719819629678436794910510259518359026273676830', '34268264483206187164571437885', '25425363284463910957419549722021124450832239517990785975889689633068548096', '12448278679517811784508557734102986855579744384337338465055621486538311281772', '9260608685281348956030279125705000716237952776955782848598673606545494194823'] - }, + { + // schemaHash + contractInput: [ + "75118319212313495155413841331241344325", + 0, + false, + false, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + expectedClaims: [ + "75118319212313495155413841331241344325", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + ], + }, + { + // id index + contractInput: [ + "75118319212313495155413841331241344325", + 1, + false, + false, + 0, + 0, + "25425363284463910957419549722021124450832239517990785975889689633068548096", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + expectedClaims: [ + "755683053054190422082163056194777767237", + "25425363284463910957419549722021124450832239517990785975889689633068548096", + "0", + "0", + "0", + "0", + "0", + "0", + ], + }, + { + // id value + contractInput: [ + "75118319212313495155413841331241344325", + 2, + false, + false, + 0, + 0, + "25425363284463910957419549722021124450832239517990785975889689633068548096", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + expectedClaims: [ + "1095965419975128885545537663626545978693", + "0", + "0", + "0", + "0", + "25425363284463910957419549722021124450832239517990785975889689633068548096", + "0", + "0", + ], + }, + { + // expirationDate + contractInput: [ + "75118319212313495155413841331241344325", + 2, + true, + false, + 0, + 0, + "25425363284463910957419549722021124450832239517990785975889689633068548096", + 0, + 1857686340, + 0, + 0, + 0, + 0, + 0, + ], + expectedClaims: [ + "3818224355342636593252534523080691670341", + "0", + "0", + "0", + "34268264483206187164568125440", + "25425363284463910957419549722021124450832239517990785975889689633068548096", + "0", + "0", + ], + }, + { + // updatableFlag + contractInput: [ + "75118319212313495155413841331241344325", + 2, + true, + true, + 0, + 0, + "25425363284463910957419549722021124450832239517990785975889689633068548096", + 0, + 1857686340, + 0, + 0, + 0, + 0, + 0, + ], + expectedClaims: [ + "9262742226077652008666528241988983053637", + "0", + "0", + "0", + "34268264483206187164568125440", + "25425363284463910957419549722021124450832239517990785975889689633068548096", + "0", + "0", + ], + }, + { + // merklized index + contractInput: [ + "75118319212313495155413841331241344325", + 2, + true, + true, + 1, + 0, + "25425363284463910957419549722021124450832239517990785975889689633068548096", + 0, + 1857686340, + "93352129123234552352342342353456456452342343456345234121567843345", + 0, + 0, + 0, + 0, + ], + expectedClaims: [ + "20151777967547682839494515679805565820229", + "0", + "93352129123234552352342342353456456452342343456345234121567843345", + "0", + "34268264483206187164568125440", + "25425363284463910957419549722021124450832239517990785975889689633068548096", + "0", + "0", + ], + }, + { + // merklized value + contractInput: [ + "75118319212313495155413841331241344325", + 2, + true, + true, + 2, + 0, + "25425363284463910957419549722021124450832239517990785975889689633068548096", + 0, + 1857686340, + "93352129123234552352342342353456456452342343456345234121567843345", + 0, + 0, + 0, + 0, + ], + expectedClaims: [ + "31040813709017713670322503117622148586821", + "0", + "0", + "0", + "34268264483206187164568125440", + "25425363284463910957419549722021124450832239517990785975889689633068548096", + "93352129123234552352342342353456456452342343456345234121567843345", + "0", + ], + }, + { + // version + contractInput: [ + "75118319212313495155413841331241344325", + 2, + true, + true, + 2, + 89220123, + "25425363284463910957419549722021124450832239517990785975889689633068548096", + 0, + 1857686340, + "93352129123234552352342342353456456452342343456345234121567843345", + 0, + 0, + 0, + 0, + ], + expectedClaims: [ + "130395355847364581104005408845894865439016836786170092869", + "0", + "0", + "0", + "34268264483206187164568125440", + "25425363284463910957419549722021124450832239517990785975889689633068548096", + "93352129123234552352342342353456456452342343456345234121567843345", + "0", + ], + }, + { + // revocation nonce + contractInput: [ + "75118319212313495155413841331241344325", + 2, + true, + true, + 2, + 89220123, + "25425363284463910957419549722021124450832239517990785975889689633068548096", + 3312445, + 1857686340, + "93352129123234552352342342353456456452342343456345234121567843345", + 0, + 0, + 0, + 0, + ], + expectedClaims: [ + "130395355847364581104005408845894865439016836786170092869", + "0", + "0", + "0", + "34268264483206187164571437885", + "25425363284463910957419549722021124450832239517990785975889689633068548096", + "93352129123234552352342342353456456452342343456345234121567843345", + "0", + ], + }, + { + // data slots + contractInput: [ + "75118319212313495155413841331241344325", + 2, + true, + true, + 0, + 89220123, + "25425363284463910957419549722021124450832239517990785975889689633068548096", + 3312445, + 1857686340, + "0", + "16243864111864693853212588481963275789994876191154110553066821559749894481761", + "7078462697308959301666117070269719819629678436794910510259518359026273676830", + "12448278679517811784508557734102986855579744384337338465055621486538311281772", + "9260608685281348956030279125705000716237952776955782848598673606545494194823", + ], + expectedClaims: [ + "130395355847364559325933925905833203783041961153004559685", + "0", + "16243864111864693853212588481963275789994876191154110553066821559749894481761", + "7078462697308959301666117070269719819629678436794910510259518359026273676830", + "34268264483206187164571437885", + "25425363284463910957419549722021124450832239517990785975889689633068548096", + "12448278679517811784508557734102986855579744384337338465055621486538311281772", + "9260608685281348956030279125705000716237952776955782848598673606545494194823", + ], + }, ]; for (let i = 0; i < inputs.length; i++) { - const input = inputs[i]; - const claims = await identity.buildClaim(input.contractInput); - // console.log(claims); - claims.forEach((c, cIndex) => { - expect(c == input.expectedClaims[cIndex]).to.be.true; - }); + const input = inputs[i]; + const claims = await identity.buildClaim(input.contractInput); + // console.log(claims); + claims.forEach((c, cIndex) => { + expect(c == input.expectedClaims[cIndex]).to.be.true; + }); } }); it("validate buildClaim errors", async function () { const inputs = [ - { // idPosition = 0 & id not null - contractInput: ['75118319212313495155413841331241344325', 0, false, false, 0, 0, '8764639037689384765', 0, 0, 0, 0, 0, 0, 0], - expectedErr: 'id should be empty' - }, - { // idPosition = 1 & id null - contractInput: ['75118319212313495155413841331241344325', 1, false, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - expectedErr: 'id should be not empty' - }, - { // idPosition = 2 & id null - contractInput: ['75118319212313495155413841331241344325', 2, false, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - expectedErr: 'id should be not empty' - }, - { // idPosition = 3 - invalid position - contractInput: ['75118319212313495155413841331241344325', 3, false, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - expectedErr: 'invalid id position' - }, - { // expirable = false & expiration date not null - contractInput: ['75118319212313495155413841331241344325', 0, false, false, 0, 0, 0, 0, 89220123, 0, 0, 0, 0, 0], - expectedErr: 'expirationDate should be 0 for non expirable claim' - }, - { // updatable = false & version not null - contractInput: ['75118319212313495155413841331241344325', 0, false, false, 0, 12133, 0, 0, 0, 0, 0, 0, 0, 0], - expectedErr: 'version should be 0 for non updatable claim' - }, - { // merklizedRootPosition = 1 & indx data slot not null - contractInput: ['75118319212313495155413841331241344325', 0, false, false, 1, 0, 0, 0, 0, 0, 1234421, 0, 0, 0], - expectedErr: 'data slots should be empty' - }, - { // merklizedRootPosition = 2 & value data slot not null - contractInput: ['75118319212313495155413841331241344325', 0, false, false, 1, 0, 0, 0, 0, 0, 0, 0, 123445, 0], - expectedErr: 'data slots should be empty' - }, - { // merklizedRootPosition = 0 & merklizedRoot not null - contractInput: ['75118319212313495155413841331241344325', 0, false, false, 0, 0, 0, 0, 0, '972355817823445311', 0, 0, 0, 0], - expectedErr: 'merklizedRoot should be 0 for non merklized claim' - }, + { + // idPosition = 0 & id not null + contractInput: [ + "75118319212313495155413841331241344325", + 0, + false, + false, + 0, + 0, + "8764639037689384765", + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + expectedErr: "IdShouldBeEmpty", + }, + { + // idPosition = 1 & id null + contractInput: [ + "75118319212313495155413841331241344325", + 1, + false, + false, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + expectedErr: "IdShouldBeNotEmpty", + }, + { + // idPosition = 2 & id null + contractInput: [ + "75118319212313495155413841331241344325", + 2, + false, + false, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + expectedErr: "IdShouldBeNotEmpty", + }, + { + // idPosition = 3 - invalid position + contractInput: [ + "75118319212313495155413841331241344325", + 3, + false, + false, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + expectedErr: "InvalidIdPosition", + }, + { + // expirable = false & expiration date not null + contractInput: [ + "75118319212313495155413841331241344325", + 0, + false, + false, + 0, + 0, + 0, + 0, + 89220123, + 0, + 0, + 0, + 0, + 0, + ], + expectedErr: "ExpirationDateShouldBeZeroForNonExpirableClaim", + }, + { + // updatable = false & version not null + contractInput: [ + "75118319212313495155413841331241344325", + 0, + false, + false, + 0, + 12133, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + expectedErr: "VersionShouldBeZeroForNonUpdatableClaim", + }, + { + // merklizedRootPosition = 1 & indx data slot not null + contractInput: [ + "75118319212313495155413841331241344325", + 0, + false, + false, + 1, + 0, + 0, + 0, + 0, + 0, + 1234421, + 0, + 0, + 0, + ], + expectedErr: "DataSlotsShouldBeEmpty", + }, + { + // merklizedRootPosition = 2 & value data slot not null + contractInput: [ + "75118319212313495155413841331241344325", + 0, + false, + false, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 123445, + 0, + ], + expectedErr: "DataSlotsShouldBeEmpty", + }, + { + // merklizedRootPosition = 0 & merklizedRoot not null + contractInput: [ + "75118319212313495155413841331241344325", + 0, + false, + false, + 0, + 0, + 0, + 0, + 0, + "972355817823445311", + 0, + 0, + 0, + 0, + ], + expectedErr: "MerklizedRootShouldBeZeroForNonMerklizedClaim", + }, ]; for (let i = 0; i < inputs.length; i++) { - const input = inputs[i]; - try { - await identity.buildClaim(input.contractInput); - expect.fail('The transaction should have thrown an error'); - } catch (err: any) { - expect(err.message).to.include(input.expectedErr); - } + const input = inputs[i]; + try { + await identity.buildClaim(input.contractInput); + expect.fail("The transaction should have thrown an error"); + } catch (err: any) { + expect(err.message).to.include(input.expectedErr); + } } - }); it("validate buildClaim from file", async function () { - var inputs: any[] = JSON.parse(fs.readFileSync(require.resolve('./vectorsGen/data/claimBuilderData.json'), 'utf-8')) - console.log(inputs.length) + var inputs: any[] = JSON.parse( + fs.readFileSync(require.resolve("./vectorsGen/data/claimBuilderData.json"), "utf-8"), + ); + console.log(inputs.length); for (let i = 0; i < inputs.length; i++) { - const input = inputs[i]; - const claims = await identity.buildClaim(input.contractInput); - claims.forEach((c, cIndex) => { - expect(c == input.expectedClaims[cIndex]).to.be.true; - }); + const input = inputs[i]; + const claims = await identity.buildClaim(input.contractInput); + claims.forEach((c, cIndex) => { + expect(c == input.expectedClaims[cIndex]).to.be.true; + }); } }); - }); diff --git a/test/onchain-identity/onchain-identity.test.ts b/test/onchain-identity/onchain-identity.test.ts index d9fc000e..b8bee4a2 100644 --- a/test/onchain-identity/onchain-identity.test.ts +++ b/test/onchain-identity/onchain-identity.test.ts @@ -616,7 +616,7 @@ describe("Genesis state doens't have history of states", () => { await identity.getRootsByState(latestState); expect.fail("The transaction should have thrown an error"); } catch (err: any) { - expect(err.message).to.include("Roots for this state doesn't exist"); + expect(err.message).to.include("RootsForThisStateDoesntExist"); } }); }); diff --git a/test/primitiveUtils/primitiveUtils.test.ts b/test/primitiveUtils/primitiveUtils.test.ts index 9bad04c6..57817f11 100644 --- a/test/primitiveUtils/primitiveUtils.test.ts +++ b/test/primitiveUtils/primitiveUtils.test.ts @@ -1,6 +1,5 @@ import { DeployHelper } from "../../helpers/DeployHelper"; import { expect } from "chai"; -import { ethers } from "hardhat"; let utilsWrapper; @@ -38,10 +37,9 @@ describe("uint conversions", function () { }); it("invalid challenge (uint256 LE address) must produce error", async () => { - const address = "0x3930000000000000000000000000000000000000"; const uint256 = "5010846606798320903600395684540411235907858077292797642081699116"; await expect(utilsWrapper.uint256LEToAddress(uint256)).to.be.rejectedWith( - "given uint256 is not a representation of an address, 12 most significant bytes should be zero", + "GivenInputNotAnAddressRepresentation(5010846606798320903600395684540411235907858077292797642081699116)", ); }); diff --git a/test/smtLib/smtLib.test.ts b/test/smtLib/smtLib.test.ts index be6181ec..e750f8b2 100644 --- a/test/smtLib/smtLib.test.ts +++ b/test/smtLib/smtLib.test.ts @@ -982,7 +982,7 @@ describe("Merkle tree proofs of SMT", () => { description: "Negative: add two leaves with maximum depth + 1 (less significant bits SET)", leavesToInsert: [ { i: genMaxBinaryNumber(64), v: 100 }, //1111111111111111111111111111111111111111111111111111111111111111 - { i: genMaxBinaryNumber(65), v: 100, error: "Max depth reached" }, //11111111111111111111111111111111111111111111111111111111111111111 + { i: genMaxBinaryNumber(65), v: 100, error: "MaxDepthReached()" }, //11111111111111111111111111111111111111111111111111111111111111111 ], }, { @@ -990,7 +990,7 @@ describe("Merkle tree proofs of SMT", () => { "Negative: add two leaves with maximum depth + 1 (less significant bits NOT SET)", leavesToInsert: [ { i: 0, v: 100 }, - { i: genMaxBinaryNumber(64) + BigInt(1), v: 100, error: "Max depth reached" }, // 10000000000000000000000000000000000000000000000000000000000000000 + { i: genMaxBinaryNumber(64) + BigInt(1), v: 100, error: "MaxDepthReached()" }, // 10000000000000000000000000000000000000000000000000000000000000000 ], }, { @@ -998,7 +998,7 @@ describe("Merkle tree proofs of SMT", () => { "Negative: add two leaves with maximum depth + 1 (less significant bits are both SET and NOT SET", leavesToInsert: [ { i: "17713686966169915918", v: 100 }, //1111010111010011101010000111000111010001000001100101001000001110 - { i: "36160431039879467534", v: 100, error: "Max depth reached" }, //11111010111010011101010000111000111010001000001100101001000001110 + { i: "36160431039879467534", v: 100, error: "MaxDepthReached()" }, //11111010111010011101010000111000111010001000001100101001000001110 ], }, ]; @@ -1061,16 +1061,16 @@ describe("Root history requests", function () { }); it("should revert if length is zero", async () => { - await expect(smt.getRootHistory(0, 0)).to.be.rejectedWith("Length should be greater than 0"); + await expect(smt.getRootHistory(0, 0)).to.be.rejectedWith("LenghtShouldBeGreaterThanZero()"); }); it("should revert if length limit exceeded", async () => { - await expect(smt.getRootHistory(0, 10 ** 6)).to.be.rejectedWith("Length limit exceeded"); + await expect(smt.getRootHistory(0, 10 ** 6)).to.be.rejectedWith("LengthLimitExceeded(1000)"); }); it("should revert if out of bounds", async () => { await expect(smt.getRootHistory(historyLength, 100)).to.be.rejectedWith( - "Start index out of bounds", + "StartIndexOutOfBounds(3)", ); }); @@ -1126,7 +1126,7 @@ describe("Root history duplicates", function () { const riDoubleRoot = await smt.getRootInfoListByRoot(doubleRoot, 0, 100); const riTripleRoot = await smt.getRootInfoListByRoot(tripleRoot, 0, 100); await expect(smt.getRootInfoListByRoot(nonExistingRoot, 0, 100)).to.be.rejectedWith( - "Root does not exist", + "RootDoesNotExist()", ); expect(riSingleRoot.length).to.be.equal(1); @@ -1158,7 +1158,7 @@ describe("Root history duplicates", function () { await smt.add(1, 1); const root = await smt.getRoot(); await expect(smt.getRootInfoListByRoot(root, 0, 0)).to.be.rejectedWith( - "Length should be greater than 0", + "LenghtShouldBeGreaterThanZero()", ); }); @@ -1166,7 +1166,7 @@ describe("Root history duplicates", function () { await smt.add(1, 1); const root = await smt.getRoot(); await expect(smt.getRootInfoListByRoot(root, 0, 10 ** 6)).to.be.rejectedWith( - "Length limit exceeded", + "LengthLimitExceeded(1000)", ); }); @@ -1176,7 +1176,7 @@ describe("Root history duplicates", function () { await smt.add(1, 1); const root = await smt.getRoot(); await expect(smt.getRootInfoListByRoot(root, 3, 100)).to.be.rejectedWith( - "Start index out of bounds", + "StartIndexOutOfBounds(2)", ); }); @@ -1769,14 +1769,14 @@ describe("Edge cases with exceptions", () => { await smt.add(1, 1); const root = await smt.getRoot(); await expect(smt.getRootInfo(root)).not.to.be.rejected; - await expect(smt.getRootInfo(root + 1n)).to.be.rejectedWith("Root does not exist"); + await expect(smt.getRootInfo(root + 1n)).to.be.rejectedWith("RootDoesNotExist()"); }); it("getProofByRoot() should throw when root does not exist", async () => { await smt.add(1, 1); const root = await smt.getRoot(); await expect(smt.getProofByRoot(1, root)).not.to.be.rejected; - await expect(smt.getProofByRoot(1, root + 1n)).to.be.rejectedWith("Root does not exist"); + await expect(smt.getProofByRoot(1, root + 1n)).to.be.rejectedWith("RootDoesNotExist()"); }); }); @@ -1804,22 +1804,20 @@ describe("maxDepth setting tests", () => { }); it("Should throw when decrease max depth", async () => { - await expect(smt.setMaxDepth(127)).to.be.rejectedWith("Max depth can only be increased"); + await expect(smt.setMaxDepth(127)).to.be.rejectedWith("MaxDepthCanOnlyBeIncreased()"); }); it("Should throw when max depth is set to the same value", async () => { - await expect(smt.setMaxDepth(128)).to.be.rejectedWith("Max depth can only be increased"); + await expect(smt.setMaxDepth(128)).to.be.rejectedWith("MaxDepthCanOnlyBeIncreased()"); }); it("Should throw when max depth is set to 0", async () => { - await expect(smt.setMaxDepth(0)).to.be.rejectedWith("Max depth must be greater than zero"); + await expect(smt.setMaxDepth(0)).to.be.rejectedWith("MaxDepthMustBeGreaterThanZero()"); }); it("Should throw when max depth is set to greater than hard cap", async () => { - await expect(smt.setMaxDepth(257)).to.be.rejectedWith("Max depth is greater than hard cap"); - await expect(smt.setMaxDepth(1000000000)).to.be.rejectedWith( - "Max depth is greater than hard cap", - ); + await expect(smt.setMaxDepth(257)).to.be.rejectedWith("MaxDepthIsGreaterThanHardCap()"); + await expect(smt.setMaxDepth(1000000000)).to.be.rejectedWith("MaxDepthIsGreaterThanHardCap()"); }); }); diff --git a/test/state/state.test.ts b/test/state/state.test.ts index 51e30f98..8d591174 100644 --- a/test/state/state.test.ts +++ b/test/state/state.test.ts @@ -44,8 +44,9 @@ describe("State transition with real groth16 verifier", () => { const modifiedStateTransition = JSON.parse(JSON.stringify(stateTransitionsWithProofs[0])); modifiedStateTransition.pub_signals[2] = "100"; // change state to make zk proof invalid - await expect(publishState(state, modifiedStateTransition)).to.be.rejectedWith( - "Zero-knowledge proof of state transition is not valid", + await expect(publishState(state, modifiedStateTransition)).to.be.revertedWithCustomError( + state, + "ZeroKnowledgeProofOfStateTransitionIsNotValid", ); }); @@ -136,9 +137,9 @@ describe("State transition negative cases", () => { const modifiedStateTransition = JSON.parse(JSON.stringify(stateTransitionsWithNoProofs[1])); modifiedStateTransition.oldState = 10; - await expect(publishStateWithStubProof(state, modifiedStateTransition)).to.be.rejectedWith( - "Old state does not match the latest state", - ); + await expect( + publishStateWithStubProof(state, modifiedStateTransition), + ).to.be.revertedWithCustomError(state, "OldStateDoesNotMatchTheLatestState"); }); it("Old state is genesis but identity already exists", async () => { @@ -147,36 +148,36 @@ describe("State transition negative cases", () => { const modifiedStateTransition = JSON.parse(JSON.stringify(stateTransitionsWithNoProofs[1])); modifiedStateTransition.isOldStateGenesis = true; - await expect(publishStateWithStubProof(state, modifiedStateTransition)).to.be.rejectedWith( - "Old state is genesis but identity already exists", - ); + await expect( + publishStateWithStubProof(state, modifiedStateTransition), + ).to.be.revertedWithCustomError(state, "OldStateIsGenesisButIdentityAlreadyExists"); }); it("Old state is not genesis but identity does not yet exist", async () => { const modifiedStateTransition = JSON.parse(JSON.stringify(stateTransitionsWithNoProofs[0])); modifiedStateTransition.isOldStateGenesis = false; - await expect(publishStateWithStubProof(state, modifiedStateTransition)).to.be.rejectedWith( - "Old state is not genesis but identity does not yet exist", - ); + await expect( + publishStateWithStubProof(state, modifiedStateTransition), + ).to.be.revertedWithCustomError(state, "OldStateIsNotGenesisButIdentityDoesNotExist"); }); it("ID should not be zero", async () => { const modifiedStateTransition = JSON.parse(JSON.stringify(stateTransitionsWithNoProofs[0])); modifiedStateTransition.id = 0; - await expect(publishStateWithStubProof(state, modifiedStateTransition)).to.be.rejectedWith( - "ID should not be zero", - ); + await expect( + publishStateWithStubProof(state, modifiedStateTransition), + ).to.be.revertedWithCustomError(state, "IdShouldNotBeZero"); }); it("New state should not be zero", async () => { const modifiedStateTransition = JSON.parse(JSON.stringify(stateTransitionsWithNoProofs[0])); modifiedStateTransition.newState = 0; - await expect(publishStateWithStubProof(state, modifiedStateTransition)).to.be.rejectedWith( - "New state should not be zero", - ); + await expect( + publishStateWithStubProof(state, modifiedStateTransition), + ).to.be.revertedWithCustomError(state, "NewStateShouldNotBeZero"); }); it("Should allow only one unique state per identity", async () => { @@ -190,8 +191,9 @@ describe("State transition negative cases", () => { isOldStateGenesis: false, }; - await expect(publishStateWithStubProof(state, stateTransition)).to.be.rejectedWith( - "New state already exists", + await expect(publishStateWithStubProof(state, stateTransition)).to.be.revertedWithCustomError( + state, + "NewStateAlreadyExists", ); }); }); @@ -433,12 +435,14 @@ describe("Check replacedAt timestamp expirations", () => { expect(await state.getGistRootReplacedAt("0x0112", 0)).to.be.equal(0); - await expect(state.getGistRootReplacedAt("0x0112", 10)).to.be.rejectedWith( - "GIST root entry not found", + await expect(state.getGistRootReplacedAt("0x0112", 10)).to.be.revertedWithCustomError( + state, + "GistRootEntryNotFound", ); - await expect(state.getGistRootReplacedAt("0x0212", 10)).to.be.rejectedWith( - "Cross-chain GIST root not found", + await expect(state.getGistRootReplacedAt("0x0212", 10)).to.be.revertedWithCustomError( + state, + "CrossChainGistRootNotFound", ); expect( @@ -453,13 +457,13 @@ describe("Check replacedAt timestamp expirations", () => { BigInt("0xD9C10A0BFB514F30B64E115D7EEB3D547C240C104E03D4548375669FE1201"), 0, ), - ).to.be.rejectedWith("State entry not found"); + ).to.be.revertedWithCustomError(state, "StateEntryNotFound"); await expect( state.getStateReplacedAt( BigInt("0xD9C10A0BFB514F30B64E115D7EEB3D547C240C104E03D4548375669FE1202"), 0, ), - ).to.be.rejectedWith("Cross-chain state not found"); + ).to.be.revertedWithCustomError(state, "CrossChainStateNotFound"); }); }); diff --git a/test/state/stateV2_v3_migration.test.ts b/test/state/stateV2_v3_migration.test.ts deleted file mode 100644 index eae3a7b2..00000000 --- a/test/state/stateV2_v3_migration.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { expect } from "chai"; -import { ethers, network} from "hardhat"; -import { publishState } from "../utils/state-utils"; -import { DeployHelper } from "../../helpers/DeployHelper"; -import { StateContractMigrationHelper } from "../../helpers/StateContractMigrationHelper"; - -const stateTransitionsWithProofs = [ - require("./data/user_state_genesis_transition.json"), - require("./data/user_state_next_transition.json"), - ]; - - const stateTransitionsWithNoProofs = [ - { - id: '6901746346790563787434755862277025452451108972170386555162524223864832', - oldState: '1099511627776', - newState: '2199023255552', - isOldStateGenesis: true, - }, - { - id: '6901746346790563787434755862277025452451108972170386555162524223864832', - oldState: '2199023255552', - newState: '3298534883328', - isOldStateGenesis: false, - }, - ]; - -describe.skip("Get State old Contract and migrate to latest version", () => { - let guWrpr; - let deployHelper; - let signers; - - before(async function () { - signers = await ethers.getSigners(); - deployHelper = await DeployHelper.initialize(); - guWrpr = await deployHelper.deployGenesisUtilsWrapper(); - }); - - it("Check migration", async () => { - // 1. init old contract by abi & address - const stateContractMigrationHelper = new StateContractMigrationHelper(deployHelper, signers[0]); - const oldContractABI = []; // abi of contract that will be upgraded - const stateContractAddress = ''; // address of contract that will be upgraded - const stateContractInstance = await stateContractMigrationHelper.getInitContract({ - contractNameOrAbi: oldContractABI, - address: stateContractAddress, - }); - - // 2. publish first state - const params1 = await publishState(stateContractInstance, stateTransitionsWithProofs[0]); - const res1 = await stateContractInstance.getStateInfoById(params1.id); - expect(res1.state).to.be.equal(BigInt(params1.newState).toString()); - - // 3. migrate - const { state: stateV3 } = await stateContractMigrationHelper.upgradeContract(stateContractInstance); - - // 4. publish second state - const params2 = await publishState(stateV3, stateTransitionsWithProofs[1]); - const res2 = await stateV3.getStateInfoById(params2.id); - expect(res2.state).to.be.equal(BigInt(params2.newState).toString()); - - // 5. check _defaultIdType is not initialized - await expect(stateV3.getDefaultIdType()).to.be.rejectedWith( - "Default Id Type is not initialized", - ); - // 6. initialize _defaultIdType - const { defaultIdType } = await deployHelper.getDefaultIdType(); - await stateV3.setDefaultIdType(defaultIdType); - const defIdTypeValue = await stateV3.getDefaultIdType(); - expect(defaultIdType).to.be.equal(defIdTypeValue); - - // 7. run new 'transitStateGeneric' method - const onchainId = await guWrpr.calcOnchainIdFromAddress( - defaultIdType, - await signers[0].getAddress(), - ); - await stateV3.transitStateGeneric( - onchainId, - stateTransitionsWithNoProofs[0].oldState, - stateTransitionsWithNoProofs[0].newState, - stateTransitionsWithNoProofs[0].isOldStateGenesis, - 1, - [] - ); - - const res3 = await stateV3.getStateInfoById(onchainId); - expect(res3.state).to.be.equal(bigInt(stateTransitionsWithNoProofs[0].newState).toString()); - - }); -}); diff --git a/test/stateLib/stateLib.test.ts b/test/stateLib/stateLib.test.ts index d23cc8f7..0600ff00 100644 --- a/test/stateLib/stateLib.test.ts +++ b/test/stateLib/stateLib.test.ts @@ -27,24 +27,24 @@ describe("Negative tests", function () { it("getStateInfoByID: should be reverted if identity does not exist", async () => { const missingID = 777; - await expect(stateLibWrpr.getStateInfoById(missingID)).to.be.revertedWith( - "Identity does not exist", + await expect(stateLibWrpr.getStateInfoById(missingID)).to.be.rejectedWith( + "IdentityDoesNotExist()", ); }); it("getStateInfoHistoryById: should be reverted if identity does not exist", async () => { const missingID = 777; - await expect(stateLibWrpr.getStateInfoHistoryById(missingID, 0, 1)).to.be.revertedWith( - "Identity does not exist", + await expect(stateLibWrpr.getStateInfoHistoryById(missingID, 0, 1)).to.be.rejectedWith( + "IdentityDoesNotExist()", ); }); it("getStateInfoHistoryLengthById: should be reverted if identity does not exist", async () => { const missingID = 777; - await expect(stateLibWrpr.getStateInfoHistoryLengthById(missingID)).to.be.revertedWith( - "Identity does not exist", + await expect(stateLibWrpr.getStateInfoHistoryLengthById(missingID)).to.be.rejectedWith( + "IdentityDoesNotExist()", ); }); @@ -52,21 +52,17 @@ describe("Negative tests", function () { const id = id1Inputs[0].id; const missingState = 888; - await expect(stateLibWrpr.getStateInfoByIdAndState(id, missingState)).to.be.revertedWith( - "State does not exist", + await expect(stateLibWrpr.getStateInfoByIdAndState(id, missingState)).to.be.rejectedWith( + "StateDoesNotExist()", ); }); it("Zero timestamp and block should be only in the first identity state", async () => { await expect(stateLibWrpr.addGenesisState(2, 20)).to.be.not.reverted; - await expect(stateLibWrpr.addGenesisState(2, 20)).to.be.revertedWith( - "Zero timestamp and block should be only in the first identity state", - ); + await expect(stateLibWrpr.addGenesisState(2, 20)).to.be.rejectedWith("IdentityAlreadyExists()"); await expect(stateLibWrpr.addState(3, 30)).to.be.not.reverted; - await expect(stateLibWrpr.addGenesisState(3, 30)).to.be.revertedWith( - "Zero timestamp and block should be only in the first identity state", - ); + await expect(stateLibWrpr.addGenesisState(3, 30)).to.be.rejectedWith("IdentityAlreadyExists()"); }); }); @@ -117,21 +113,21 @@ describe("StateInfo history", function () { }); it("should be reverted if length is zero", async () => { - await expect(stateLibWrpr.getStateInfoHistoryById(id1, 0, 0)).to.be.revertedWith( - "Length should be greater than 0", + await expect(stateLibWrpr.getStateInfoHistoryById(id1, 0, 0)).to.be.rejectedWith( + "LenghtShouldBeGreaterThanZero()", ); }); it("should be reverted if length limit exceeded", async () => { - await expect(stateLibWrpr.getStateInfoHistoryById(id1, 0, 10 ** 6)).to.be.revertedWith( - "Length limit exceeded", + await expect(stateLibWrpr.getStateInfoHistoryById(id1, 0, 10 ** 6)).to.be.rejectedWith( + "LengthLimitExceeded(1000)", ); }); it("should be reverted if startIndex is out of bounds", async () => { await expect( stateLibWrpr.getStateInfoHistoryById(id1, id1HistoryLength, 100), - ).to.be.revertedWith("Start index out of bounds"); + ).to.be.rejectedWith("StartIndexOutOfBounds(2)"); }); it("should not revert if startIndex + length >= historyLength", async () => { @@ -252,7 +248,7 @@ describe("State history duplicates", function () { const state = 1; await stateLibWrpr.addState(id, state); await expect(stateLibWrpr.getStateInfoListByIdAndState(id, state, 0, 0)).to.be.rejectedWith( - "Length should be greater than 0", + "LenghtShouldBeGreaterThanZero()", ); }); @@ -262,7 +258,7 @@ describe("State history duplicates", function () { await stateLibWrpr.addState(id, state); await expect( stateLibWrpr.getStateInfoListByIdAndState(id, state, 0, 10 ** 6), - ).to.be.revertedWith("Length limit exceeded"); + ).to.be.rejectedWith("LengthLimitExceeded(1000)"); }); it("should revert if out of bounds", async () => { @@ -271,8 +267,8 @@ describe("State history duplicates", function () { await stateLibWrpr.addState(id, state); await stateLibWrpr.addState(id, state); await stateLibWrpr.addState(id, state); - await expect(stateLibWrpr.getStateInfoListByIdAndState(id, state, 3, 100)).to.be.revertedWith( - "Start index out of bounds", + await expect(stateLibWrpr.getStateInfoListByIdAndState(id, state, 3, 100)).to.be.rejectedWith( + "StartIndexOutOfBounds(3)", ); it("should NOT revert if startIndex + length >= historyLength", async () => { diff --git a/test/utils/validator-pack-utils.ts b/test/utils/validator-pack-utils.ts index 68cde7af..900c6386 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," + ")", ], [ @@ -31,7 +31,7 @@ export function packValidatorParams(query: any, allowedIssuers: any[] = []): str skipClaimRevocationCheck: query.skipClaimRevocationCheck, claimPathNotExists: query.claimPathNotExists, }, - ] + ], ); } @@ -70,6 +70,35 @@ export function packV3ValidatorParams(query: any, allowedIssuers: any[] = []): s proofType: query.proofType, verifierID: query.verifierID, }, - ] + ], + ); +} + +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/authv2/index.ts b/test/validators/authv2/index.ts new file mode 100644 index 00000000..091f99c9 --- /dev/null +++ b/test/validators/authv2/index.ts @@ -0,0 +1,113 @@ +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"; +import { contractsInfo } from "../../../helpers/constants"; + +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 Gist root not found", + sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + stateTransitions: [ + require("../common-data/issuer_from_genesis_state_to_first_transition_v3.json"), + ], + userID: 23273167900576580892722615617815475823351560716009055944677723144398443009n, + gistRoot: 2n, + errorMessage: "GistRootEntryNotFound()", + }, +]; + +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", + 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); + + if (test.errorMessage) { + await expect(authV2validator.verify(test.sender, zkProof, data)).to.be.rejectedWith( + test.errorMessage, + ); + } else if (test.errorMessage === "") { + await expect(authV2validator.verify(test.sender, zkProof, data)).to.be.reverted; + } else { + const result = await authV2validator.verify(test.sender, zkProof, data); + + expect(result[0]).to.be.equal(test.userID); + } + }); + } + + it("check version", async () => { + const version = await authV2validator.version(); + expect(version).to.be.equal(contractsInfo.VALIDATOR_AUTH_V2.version); + }); +}); 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..794b7d2c --- /dev/null +++ b/test/validators/linked-multi-query/linked-multi-query.test.ts @@ -0,0 +1,194 @@ +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"; +import { contractsInfo } from "../../../helpers/constants"; + +describe("Test linkedMultiQuery10.circom", function () { + let validator, groth16Verifier; + let signer; + + const stateAddress = ethers.ZeroAddress; + + const linkId = "1"; + const merklized = "1"; + const operator1 = "1"; + const operator2 = "16"; + const operatorOutput1 = "0"; + const operatorOutput2 = "777"; + const queryHash1 = "100"; + const queryHash2 = "200"; + + const dummyZKProof = [ + ["0", "0"], + [ + ["0", "0"], + ["0", "0"], + ], + ["0", "0"], + ]; + + const proofForOneQuery = packZKProof( + [linkId, merklized] + .concat([operatorOutput1, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + .concat([queryHash1, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ...dummyZKProof, + ); + + const proofForTwoQueries = packZKProof( + [linkId, merklized] + .concat([operatorOutput1, operatorOutput2, 0, 0, 0, 0, 0, 0, 0, 0]) + .concat([queryHash1, queryHash2, 0, 0, 0, 0, 0, 0, 0, 0]), + ...dummyZKProof, + ); + + const oneQuery = { + claimPathKey: [0, 0], + operator: [operator1], + slotIndex: [0, 0], + value: [ + [0, 0], + [0, 0], + ], + queryHash: [queryHash1], + circuitIds: ["linkedMultiQuery10-beta.1"], + groupID: 1, + verifierID: 1, + }; + + const oneQueryParams = packLinkedMultiQueryValidatorParams(oneQuery); + + const twoQueries = { + claimPathKey: [0, 0], + operator: [operator1, operator2], + slotIndex: [0, 0], + value: [ + [0, 0], + [0, 0], + ], + queryHash: [queryHash1, queryHash2], + circuitIds: ["linkedMultiQuery10-beta.1"], + groupID: 1, + verifierID: 1, + }; + + const twoQueriesParams = packLinkedMultiQueryValidatorParams(twoQueries); + + 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); + return { validator, groth16Verifier }; + } + + beforeEach(async () => { + ({ validator, groth16Verifier } = await loadFixture(deployContractsFixture)); + }); + + it("Should verify", async function () { + const result = await validator.verify(signer.address, proofForTwoQueries, twoQueriesParams); + expect(result).to.deep.equal([ + ["linkID", linkId], + ["operatorOutput1", 777n], + ]); + + // have more than one operator output + }); + + it("Should throw if circuitId is not linkedMultiQuery10-beta.1", async function () { + const params = packLinkedMultiQueryValidatorParams({ + ...oneQuery, + circuitIds: ["someWrongCircuitId"], + }); + + await expect(validator.verify(signer.address, proofForOneQuery, params)) + .to.be.revertedWithCustomError(validator, "WrongCircuitID") + .withArgs("someWrongCircuitId"); + }); + + it("More than 10 queries in request params should throw", async function () { + const params = packLinkedMultiQueryValidatorParams({ + ...oneQuery, + queryHash: Array(11).fill(queryHash1), + }); + + await expect(validator.verify(signer.address, proofForOneQuery, params)) + .to.be.revertedWithCustomError(validator, "TooManyQueries") + .withArgs(11); + }); + + it("Should throw if wrong query hash", async function () { + const params = packLinkedMultiQueryValidatorParams({ + ...oneQuery, + queryHash: [queryHash2], + }); + + await expect(validator.verify(signer.address, proofForOneQuery, params)) + .to.be.revertedWithCustomError(validator, "InvalidQueryHash") + .withArgs(queryHash2, queryHash1); + }); + + it("Should throw if groupID is 0", async function () { + const params = packLinkedMultiQueryValidatorParams({ + ...oneQuery, + groupID: 0, + }); + + await expect(validator.verify(signer.address, proofForOneQuery, params)) + .to.be.revertedWithCustomError(validator, "InvalidGroupID") + .withArgs(0); + }); + + it("getRequestParams should return the correct values", async function () { + const result = await validator.getRequestParams(oneQueryParams); + expect(result).to.deep.equal([ + ["groupID", oneQuery.groupID], + ["verifierID", oneQuery.verifierID], + ["nullifierSessionID", 0], + ]); + }); + + it("Should throw if failed ZK verification", async function () { + await groth16Verifier.stub_setVerifyResult(false); + + await expect( + validator.verify(signer.address, proofForOneQuery, oneQueryParams), + ).to.be.revertedWithCustomError(validator, "InvalidGroth16Proof"); + }); + + it("Contract version should be 1.0.0-beta.1", async function () { + expect(await validator.VERSION()).to.equal("1.0.0-beta.1"); + expect(await validator.version()).to.equal("1.0.0-beta.1"); + }); + + it("check version", async () => { + const version = await validator.version(); + expect(version).to.be.equal(contractsInfo.VALIDATOR_LINKED_MULTI_QUERY.version); + }); + + it("check getRequestParams", async () => { + const query: any = { + claimPathKey: [1, 2], + operator: [2, 3], + slotIndex: [0], + value: [ + [20020101, ...new Array(63).fill(0)], + [20030101, ...new Array(63).fill(0)], + ], + queryHash: [3, 4], + circuitIds: ["circuitName"], + groupID: 4, + verifierID: 5, + }; + + const params = packLinkedMultiQueryValidatorParams(query); + const requestParams = await validator.getRequestParams(params); + expect(requestParams[await validator.requestParamIndexOf("groupID")][1]).to.be.equal(4); + expect(requestParams[await validator.requestParamIndexOf("verifierID")][1]).to.be.equal(5); + expect(requestParams[await validator.requestParamIndexOf("nullifierSessionID")][1]).to.be.equal( + 0, + ); + }); +}); diff --git a/test/validators/mtp/index.ts b/test/validators/mtp/index.ts index 4e341f8e..a2a67a54 100644 --- a/test/validators/mtp/index.ts +++ b/test/validators/mtp/index.ts @@ -5,8 +5,9 @@ import { packValidatorParams } from "../../utils/validator-pack-utils"; import { CircuitId } from "@0xpolygonid/js-sdk"; import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; import { time } from "@nomicfoundation/hardhat-network-helpers"; -import { TEN_YEARS } from "../../../helpers/constants"; +import { contractsInfo, TEN_YEARS } from "../../../helpers/constants"; import { packZKProof } from "../../utils/packData"; +import { ethers } from "hardhat"; const tenYears = TEN_YEARS; const testCases: any[] = [ @@ -86,7 +87,7 @@ const testCases: any[] = [ stateTransitionDelayMs: 2000, // [1....][2....][3....][4....] - each block is 2 seconds long proofJson: require("./data/valid_mtp_user_non_genesis.json"), setRevStateExpiration: 3, // [1....][2....][3..*.][4....] <-- (*) - marks where the expiration threshold is - errorMessage: "Non-Revocation state of Issuer expired", + errorMessage: "NonRevocationStateOfIssuerIsExpired()", setProofExpiration: tenYears, }, { @@ -100,7 +101,7 @@ const testCases: any[] = [ stateTransitionDelayMs: 2000, // [1....][2....][3....][4....] - each block is 2 seconds long proofJson: require("./data/valid_mtp_user_non_genesis.json"), // generated on step 2 setGISTRootExpiration: 3, // [1....][2....][3..*.][4....] <-- (*) - marks where the expiration threshold is - errorMessage: "Gist root is expired", + errorMessage: "GistRootIsExpired()", setProofExpiration: tenYears, }, { @@ -111,7 +112,7 @@ const testCases: any[] = [ require("../common-data/issuer_next_state_transition.json"), ], proofJson: require("./data/valid_mtp_user_non_genesis.json"), - errorMessage: "Generated proof is outdated", + errorMessage: "GeneratedProofIsOutdated()", }, { name: "Validate Genesis User State. Issuer Claim IdenState is in Chain. Revocation State is in Chain", @@ -119,7 +120,7 @@ const testCases: any[] = [ proofJson: require("./data/valid_mtp_user_genesis.json"), setProofExpiration: tenYears, allowedIssuers: [123n], - errorMessage: "Issuer is not on the Allowed Issuers list", + errorMessage: "IssuerIsNotOnTheAllowedIssuersList()", }, ]; @@ -206,39 +207,15 @@ 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()), - ).to.be.rejectedWith(test.errorMessage); + await expect(mtpValidator.verify(senderAddress, zkProof, data)).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(senderAddress, zkProof, data)).to.be.reverted; } else { - const signals = await mtpValidator.verifyV2( - zkProof, - data, - senderAddress, - await state.getAddress(), - ); + const signals = await mtpValidator.verify(senderAddress, zkProof, data); checkSignals(signals, test.signalValues); } }); @@ -248,4 +225,49 @@ describe("Atomic MTP Validator", function () { const challengeIndx = await mtpValidator.inputIndexOf("challenge"); expect(challengeIndx).to.be.equal(4); }); + + it("check version", async () => { + const version = await mtpValidator.version(); + expect(version).to.be.equal(contractsInfo.VALIDATOR_MTP.version); + }); + + it("check getRequestParams", async () => { + const query: any = { + requestId: 1, + schema: 2, + claimPathKey: 3, + operator: 4, + slotIndex: 0, + queryHash: 5, + value: [20020101, ...new Array(63).fill(0)], // for operators 1-3 only first value matters + circuitIds: ["circuitName"], + skipClaimRevocationCheck: false, + claimPathNotExists: 0, + }; + + const params = packValidatorParams(query); + const requestParams = await mtpValidator.getRequestParams(params); + expect(requestParams[await mtpValidator.requestParamIndexOf("groupID")][1]).to.be.equal(0); + expect(requestParams[await mtpValidator.requestParamIndexOf("verifierID")][1]).to.be.equal(0); + expect( + requestParams[await mtpValidator.requestParamIndexOf("nullifierSessionID")][1], + ).to.be.equal(0); + }); + + it("Test get config params", async () => { + const oneHour = 3600; + const expirationTimeout = await mtpValidator.getProofExpirationTimeout(); + const revocationStateExpirationTimeout = + await mtpValidator.getRevocationStateExpirationTimeout(); + const gistRootExpirationTimeout = await mtpValidator.getGISTRootExpirationTimeout(); + expect(expirationTimeout).to.be.equal(oneHour); + expect(revocationStateExpirationTimeout).to.be.equal(oneHour); + expect(gistRootExpirationTimeout).to.be.equal(oneHour); + }); + + it("Test supported circuits", async () => { + const supportedCircuitIds = await mtpValidator.getSupportedCircuitIds(); + expect(supportedCircuitIds.length).to.be.equal(1); + expect(supportedCircuitIds[0]).to.be.equal(CircuitId.AtomicQueryMTPV2OnChain); + }); }); diff --git a/test/validators/sig/index.ts b/test/validators/sig/index.ts index 08d83bdf..0e756690 100644 --- a/test/validators/sig/index.ts +++ b/test/validators/sig/index.ts @@ -5,8 +5,9 @@ import { packValidatorParams } from "../../utils/validator-pack-utils"; import { CircuitId } from "@0xpolygonid/js-sdk"; import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; import { time } from "@nomicfoundation/hardhat-network-helpers"; -import { TEN_YEARS } from "../../../helpers/constants"; +import { contractsInfo, TEN_YEARS } from "../../../helpers/constants"; import { packZKProof } from "../../utils/packData"; +import { ethers } from "hardhat"; const tenYears = TEN_YEARS; const testCases: any[] = [ @@ -86,7 +87,7 @@ const testCases: any[] = [ stateTransitionDelayMs: 2000, // [1....][2....][3....][4....] - each block is 2 seconds long proofJson: require("./data/valid_sig_user_non_genesis.json"), setRevStateExpiration: 3, // [1....][2....][3..*.][4....] <-- (*) - marks where the expiration threshold is - errorMessage: "Non-Revocation state of Issuer expired", + errorMessage: "NonRevocationStateOfIssuerIsExpired()", setProofExpiration: tenYears, }, { @@ -100,7 +101,7 @@ const testCases: any[] = [ stateTransitionDelayMs: 2000, // [1....][2....][3....][4....] - each block is 2 seconds long proofJson: require("./data/valid_sig_user_non_genesis.json"), // generated on step 2 setGISTRootExpiration: 3, // [1....][2....][3..*.][4....] <-- (*) - marks where the expiration threshold is - errorMessage: "Gist root is expired", + errorMessage: "GistRootIsExpired()", setProofExpiration: tenYears, }, { @@ -111,7 +112,7 @@ const testCases: any[] = [ require("../common-data/issuer_next_state_transition.json"), ], proofJson: require("./data/valid_sig_user_non_genesis.json"), - errorMessage: "Generated proof is outdated", + errorMessage: "GeneratedProofIsOutdated()", }, { name: "Validate Genesis User State. Issuer Claim IdenState is in Chain. Revocation State is in Chain", @@ -119,7 +120,7 @@ const testCases: any[] = [ proofJson: require("./data/valid_sig_user_genesis.json"), setProofExpiration: tenYears, allowedIssuers: [123n], - errorMessage: "Issuer is not on the Allowed Issuers list", + errorMessage: "IssuerIsNotOnTheAllowedIssuersList()", }, ]; @@ -202,39 +203,15 @@ 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()), - ).to.be.rejectedWith(test.errorMessage); + await expect(sigValidator.verify(senderAddress, zkProof, data)).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(senderAddress, zkProof, data)).to.be.reverted; } else { - const signals = await sigValidator.verifyV2( - zkProof, - data, - senderAddress, - await state.getAddress(), - ); + const signals = await sigValidator.verify(senderAddress, zkProof, data); checkSignals(signals, test.signalValues); } }); @@ -244,4 +221,49 @@ describe("Atomic Sig Validator", function () { const challengeIndx = await sigValidator.inputIndexOf("challenge"); expect(challengeIndx).to.be.equal(5); }); + + it("check version", async () => { + const version = await sigValidator.version(); + expect(version).to.be.equal(contractsInfo.VALIDATOR_SIG.version); + }); + + it("check getRequestParams", async () => { + const query: any = { + requestId: 1, + schema: 2, + claimPathKey: 3, + operator: 4, + slotIndex: 0, + queryHash: 5, + value: [20020101, ...new Array(63).fill(0)], // for operators 1-3 only first value matters + circuitIds: ["circuitName"], + skipClaimRevocationCheck: false, + claimPathNotExists: 0, + }; + + const params = packValidatorParams(query); + const requestParams = await sigValidator.getRequestParams(params); + expect(requestParams[await sigValidator.requestParamIndexOf("groupID")][1]).to.be.equal(0); + expect(requestParams[await sigValidator.requestParamIndexOf("verifierID")][1]).to.be.equal(0); + expect( + requestParams[await sigValidator.requestParamIndexOf("nullifierSessionID")][1], + ).to.be.equal(0); + }); + + it("Test get config params", async () => { + const oneHour = 3600; + const expirationTimeout = await sigValidator.getProofExpirationTimeout(); + const revocationStateExpirationTimeout = + await sigValidator.getRevocationStateExpirationTimeout(); + const gistRootExpirationTimeout = await sigValidator.getGISTRootExpirationTimeout(); + expect(expirationTimeout).to.be.equal(oneHour); + expect(revocationStateExpirationTimeout).to.be.equal(oneHour); + expect(gistRootExpirationTimeout).to.be.equal(oneHour); + }); + + it("Test supported circuits", async () => { + const supportedCircuitIds = await sigValidator.getSupportedCircuitIds(); + expect(supportedCircuitIds.length).to.be.equal(1); + expect(supportedCircuitIds[0]).to.be.equal(CircuitId.AtomicQuerySigV2OnChain); + }); }); diff --git a/test/validators/v3/index.ts b/test/validators/v3/index.ts index 1d7f39a2..87635cf9 100644 --- a/test/validators/v3/index.ts +++ b/test/validators/v3/index.ts @@ -6,8 +6,9 @@ import { calculateQueryHashV3 } from "../../utils/query-hash-utils"; import { CircuitId } from "@0xpolygonid/js-sdk"; import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; import { time } from "@nomicfoundation/hardhat-network-helpers"; -import { TEN_YEARS } from "../../../helpers/constants"; +import { contractsInfo, TEN_YEARS } from "../../../helpers/constants"; import { packZKProof } from "../../utils/packData"; +import { ethers } from "hardhat"; const tenYears = TEN_YEARS; const testCases: any[] = [ @@ -45,7 +46,7 @@ const testCases: any[] = [ require("../common-data/issuer_from_genesis_state_to_first_transition_v3.json"), ], proofJson: require("./data/invalid_bjj_user_genesis_v3.json"), - errorMessage: "Proof is not valid", + errorMessage: "ProofIsNotValid()", setProofExpiration: tenYears, sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", }, @@ -120,7 +121,7 @@ const testCases: any[] = [ stateTransitionDelayMs: 2000, // [1....][2....][3....][4....] - each block is 2 seconds long proofJson: require("./data/valid_bjj_user_second_issuer_first_v3"), setRevStateExpiration: 3, // [1....][2....][3..*.][4....] <-- (*) - marks where the expiration threshold is - errorMessage: "Non-Revocation state of Issuer expired", + errorMessage: "NonRevocationStateOfIssuerIsExpired()", setProofExpiration: tenYears, sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", }, @@ -136,7 +137,7 @@ const testCases: any[] = [ stateTransitionDelayMs: 2000, // [1....][2....][3....][4....] - each block is 2 seconds long setGISTRootExpiration: 3, // [1....][2....][3..*.][4....] <-- (*) - marks where the expiration threshold is - errorMessage: "Gist root is expired", + errorMessage: "GistRootIsExpired()", setProofExpiration: tenYears, sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", }, @@ -148,7 +149,7 @@ const testCases: any[] = [ require("../common-data/issuer_from_first_state_to_second_transition_v3.json"), ], proofJson: require("./data/valid_bjj_user_first_v3.json"), - errorMessage: "Generated proof is outdated", + errorMessage: "GeneratedProofIsOutdated()", sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", }, { @@ -159,7 +160,7 @@ const testCases: any[] = [ proofJson: require("./data/valid_bjj_user_genesis_v3.json"), setProofExpiration: tenYears, allowedIssuers: [123n], - errorMessage: "Issuer is not on the Allowed Issuers list", + errorMessage: "IssuerIsNotOnTheAllowedIssuersList()", sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", }, { @@ -227,7 +228,7 @@ const testCases: any[] = [ require("../common-data/issuer_from_genesis_state_to_first_transition_v3.json"), ], proofJson: require("./data/invalid_mtp_user_genesis_v3.json"), - errorMessage: "Proof is not valid", + errorMessage: "ProofIsNotValid()", setProofExpiration: tenYears, isMtpProof: true, sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", @@ -304,7 +305,7 @@ const testCases: any[] = [ stateTransitionDelayMs: 2000, // [1....][2....][3....][4....] - each block is 2 seconds long proofJson: require("./data/valid_mtp_user_second_issuer_first_v3.json"), setRevStateExpiration: 3, // [1....][2....][3..*.][4....] <-- (*) - marks where the expiration threshold is - errorMessage: "Non-Revocation state of Issuer expired", + errorMessage: "NonRevocationStateOfIssuerIsExpired()", setProofExpiration: tenYears, isMtpProof: true, sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", @@ -320,7 +321,7 @@ const testCases: any[] = [ proofJson: require("./data/valid_mtp_user_first_v3"), stateTransitionDelayMs: 2000, // [1....][2....][3....][4....] - each block is 2 seconds long setGISTRootExpiration: 3, // [1....][2....][3..*.][4....] <-- (*) - marks where the expiration threshold is - errorMessage: "Gist root is expired", + errorMessage: "GistRootIsExpired()", setProofExpiration: tenYears, isMtpProof: true, sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", @@ -333,7 +334,7 @@ const testCases: any[] = [ require("../common-data/issuer_from_first_state_to_second_transition_v3.json"), ], proofJson: require("./data/valid_mtp_user_first_v3.json"), - errorMessage: "Generated proof is outdated", + errorMessage: "GeneratedProofIsOutdated()", isMtpProof: true, sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", }, @@ -345,7 +346,7 @@ const testCases: any[] = [ proofJson: require("./data/valid_mtp_user_genesis_v3.json"), setProofExpiration: tenYears, allowedIssuers: [123n], - errorMessage: "Issuer is not on the Allowed Issuers list", + errorMessage: "IssuerIsNotOnTheAllowedIssuersList()", isMtpProof: true, sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", }, @@ -387,7 +388,7 @@ const testCases: any[] = [ ], proofJson: require("./data/valid_bjj_user_genesis_auth_disabled_v3_wrong_id.json"), setProofExpiration: tenYears, - errorMessage: "UserID does not correspond to the sender", + errorMessage: "UserIDDoesNotCorrespondToTheSender()", ethereumBasedUser: true, sender: "0x6edFa588aFd58803F728AbC91984c69528C00854", }, @@ -398,7 +399,7 @@ const testCases: any[] = [ ], proofJson: require("./data/valid_mtp_user_genesis_auth_disabled_v3_wrong_id.json"), setProofExpiration: tenYears, - errorMessage: "UserID does not correspond to the sender", + errorMessage: "UserIDDoesNotCorrespondToTheSender()", ethereumBasedUser: true, isMtpProof: true, sender: "0x6edFa588aFd58803F728AbC91984c69528C00854", @@ -439,7 +440,7 @@ const testCases: any[] = [ require("../common-data/user_from_genesis_state_to_first_transition_v3.json"), ], proofJson: require("./data/valid_bjj_user_first_issuer_genesis_v3.json"), - errorMessage: "Challenge should match the sender", + errorMessage: "ChallengeShouldMatchTheSender()", setProofExpiration: tenYears, sender: "0x0000000000000000000000000000000000000000", }, @@ -452,7 +453,7 @@ const testCases: any[] = [ proofJson: require("./data/valid_bjj_user_genesis_auth_disabled_v3.json"), setProofExpiration: tenYears, ethereumBasedUser: true, - errorMessage: "Invalid Link ID pub signal", + errorMessage: "InvalidLinkIDPubSignal()", sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", groupID: 0, }, @@ -465,7 +466,7 @@ const testCases: any[] = [ proofJson: require("./data/valid_bjj_user_genesis_auth_disabled_v3.json"), setProofExpiration: tenYears, ethereumBasedUser: true, - errorMessage: "Proof type should match the requested one in query", + errorMessage: "ProofTypeShouldMatchTheRequestedOneInQuery()", sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", isMtpProof: true, }, @@ -478,7 +479,7 @@ const testCases: any[] = [ proofJson: require("./data/valid_bjj_user_genesis_auth_disabled_v3.json"), setProofExpiration: tenYears, ethereumBasedUser: true, - errorMessage: "Invalid nullify pub signal", + errorMessage: "InvalidNullifyPubSignal()", sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", nullifierSessionId: "2", }, @@ -491,14 +492,15 @@ const testCases: any[] = [ proofJson: require("./data/valid_bjj_user_genesis_auth_disabled_v3.json"), setProofExpiration: tenYears, ethereumBasedUser: true, - errorMessage: "Query hash does not match the requested one", + errorMessage: + "QueryHashDoesNotMatchTheRequestedOne(0, 19185468473610285815446195195707572856383167010831244369191309337886545428382)", sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", queryHash: BigInt(0), }, ]; describe("Atomic V3 Validator", function () { - let state: any, v3validator; + let state: any, v3Validator; async function deployContractsFixture() { const deployHelper = await DeployHelper.initialize(null, true); @@ -527,7 +529,7 @@ describe("Atomic V3 Validator", function () { } beforeEach(async () => { - ({ stateContract: state, validator: v3validator } = await loadFixture(deployContractsFixture)); + ({ stateContract: state, validator: v3Validator } = await loadFixture(deployContractsFixture)); }); for (const test of testCases) { @@ -582,57 +584,80 @@ describe("Atomic V3 Validator", function () { const { inputs, pi_a, pi_b, pi_c } = prepareInputs(test.proofJson); if (test.setProofExpiration) { - await v3validator.setProofExpirationTimeout(test.setProofExpiration); + await v3Validator.setProofExpirationTimeout(test.setProofExpiration); } if (test.setRevStateExpiration) { - await v3validator.setRevocationStateExpirationTimeout(test.setRevStateExpiration); + await v3Validator.setRevocationStateExpirationTimeout(test.setRevStateExpiration); } if (test.setGISTRootExpiration) { - await v3validator.setGISTRootExpirationTimeout(test.setGISTRootExpiration); + await v3Validator.setGISTRootExpirationTimeout(test.setGISTRootExpiration); } 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()), - ).to.be.rejectedWith(test.errorMessage); + await expect(v3Validator.verify(test.sender, zkProof, data)).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(test.sender, zkProof, data)).to.be.reverted; } else { - const signals = await v3validator.verifyV2( - zkProof, - data, - test.sender, - await state.getAddress(), - ); + const signals = await v3Validator.verify(test.sender, zkProof, data); checkSignals(signals, test.signalValues); } }); } + + it("check version", async () => { + const version = await v3Validator.version(); + expect(version).to.be.equal(contractsInfo.VALIDATOR_V3.version); + }); + + it("check getRequestParams", async () => { + const query: any = { + requestId: 1, + schema: 2, + claimPathKey: 3, + operator: 4, + slotIndex: 0, + queryHash: 5, + value: [20020101, ...new Array(63).fill(0)], // for operators 1-3 only first value matters + circuitIds: ["circuitName"], + skipClaimRevocationCheck: false, + claimPathNotExists: 0, + allowedIssuers: [], + verifierID: 7, + nullifierSessionID: 8, + groupID: 9, + proofType: 0, + }; + + const params = packV3ValidatorParams(query); + const requestParams = await v3Validator.getRequestParams(params); + expect(requestParams[await v3Validator.requestParamIndexOf("groupID")][1]).to.be.equal(9); + expect(requestParams[await v3Validator.requestParamIndexOf("verifierID")][1]).to.be.equal(7); + expect( + requestParams[await v3Validator.requestParamIndexOf("nullifierSessionID")][1], + ).to.be.equal(8); + }); + + it("Test get config params", async () => { + const oneHour = 3600; + const expirationTimeout = await v3Validator.getProofExpirationTimeout(); + const revocationStateExpirationTimeout = + await v3Validator.getRevocationStateExpirationTimeout(); + const gistRootExpirationTimeout = await v3Validator.getGISTRootExpirationTimeout(); + expect(expirationTimeout).to.be.equal(oneHour); + expect(revocationStateExpirationTimeout).to.be.equal(oneHour); + expect(gistRootExpirationTimeout).to.be.equal(oneHour); + }); + + it("Test supported circuits", async () => { + const supportedCircuitIds = await v3Validator.getSupportedCircuitIds(); + expect(supportedCircuitIds.length).to.be.equal(1); + expect(supportedCircuitIds[0]).to.be.equal(CircuitId.AtomicQueryV3OnChain); + }); }); diff --git a/test/verifier/embedded-verifier.test.ts b/test/verifier/embedded-verifier.test.ts new file mode 100644 index 00000000..adb5decd --- /dev/null +++ b/test/verifier/embedded-verifier.test.ts @@ -0,0 +1,96 @@ +import { ethers } from "hardhat"; +import { beforeEach } from "mocha"; +import { DeployHelper } from "../../helpers/DeployHelper"; +import { expect } from "chai"; + +describe("EmbeddedVerifier tests", function () { + let verifier, state, validator, signer: any; + let request, paramsFromValidator, authResponse, response, crossChainProofs: any; + + async function deployContractsFixture() { + [signer] = await ethers.getSigners(); + const deployHelper = await DeployHelper.initialize(null, true); + const verifier = await ethers.deployContract("EmbeddedVerifierWrapper", []); + + const { state } = await deployHelper.deployStateWithLibraries([], "Groth16VerifierStub"); + await verifier.initialize(await signer.getAddress(), await state.getAddress()); + + const validator = await ethers.deployContract("RequestValidatorStub"); + + const authValidator = await deployHelper.deployValidatorStub("AuthValidatorStub"); + await authValidator.stub_setVerifyResults(1); + + const authMethod = { + authMethod: "stubAuth", + validator: await authValidator.getAddress(), + params: "0x", + }; + await verifier.setAuthMethod(authMethod); + + return { state, verifier, validator }; + } + + beforeEach(async function () { + ({ state, verifier, validator } = await deployContractsFixture()); + + request = { + requestId: 0, + metadata: "0x", + validator: await validator.getAddress(), + params: "0x", + }; + + paramsFromValidator = [ + { name: "groupID", value: 0 }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 0 }, + ]; + + authResponse = { + authMethod: "stubAuth", + proof: "0x", + }; + response = { + requestId: 0, + proof: "0x", + metadata: "0x", + }; + + crossChainProofs = "0x"; + }); + + it("Test get state address", async () => { + let stateAddr = await verifier.getStateAddress(); + expect(stateAddr).to.be.equal(await state.getAddress()); + + await verifier.setState(await signer.getAddress()); + + stateAddr = await verifier.getStateAddress(); + expect(stateAddr).to.be.equal(await signer.getAddress()); + + await verifier.setState(await state.getAddress()); + }); + + it("beforeProofSubmit/afterProofSubmit when submitting response", async function () { + await validator.stub_setRequestParams([request.params], [paramsFromValidator]); + + await verifier.setRequests([request]); + + await expect(verifier.submitResponse(authResponse, [response], crossChainProofs)).to.emit( + verifier, + "BeforeProofSubmit", + ); + + let filter = verifier.filters.BeforeProofSubmit; + let events = await verifier.queryFilter(filter, -1); + expect(events[0].eventName).to.be.equal("BeforeProofSubmit"); + expect(events[0].args.authResponse).to.deep.equal(Object.values(authResponse)); + expect(events[0].args.responses).to.deep.equal([Object.values(response)]); + + filter = verifier.filters.AfterProofSubmit; + events = await verifier.queryFilter(filter, -1); + expect(events[0].eventName).to.be.equal("AfterProofSubmit"); + expect(events[0].args.authResponse).to.deep.equal(Object.values(authResponse)); + expect(events[0].args.responses).to.deep.equal([Object.values(response)]); + }); +}); diff --git a/test/verifier/embedded-zkp-verifier.test.ts b/test/verifier/embedded-zkp-verifier.test.ts deleted file mode 100644 index 418b608c..00000000 --- a/test/verifier/embedded-zkp-verifier.test.ts +++ /dev/null @@ -1,258 +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, validator: any; - let owner: Signer; - - 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, - }; - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - const metadatas = "0x"; - - 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.deployEmbeddedZKPVerifierWrapper( - owner, - await state.getAddress(), - await verifierLib.getAddress(), - ); - - validator = await deployHelper.deployValidatorStub(); - } - - async function checkStorageFields(verifier: any, requestId: number, storageFields: any[]) { - for (const field of storageFields) { - const value = await verifier.getProofStorageField( - await owner.getAddress(), - requestId, - field.name, - ); - expect(value).to.be.equal(field.value); - } - } - - beforeEach(async () => { - await loadFixture(deployContractsFixture); - }); - - it("test submit response", async () => { - await verifier.setZKPRequest(0, { - metadata: "metadata", - validator: await validator.getAddress(), - data: packValidatorParams(query), - }); - - const tx = await verifier.submitZKPResponse(0, inputs, pi_a, pi_b, pi_c); - const txRes = await tx.wait(); - const storageFields = [ - { - name: "userID", - value: inputs[1], - }, - { - name: "issuerID", - value: inputs[2], - }, - ]; - - await checkStorageFields(verifier, 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(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 validator.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 validator.getAddress()); - - 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("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", - 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, - }; - - await verifier.setZKPRequest(0, { - metadata: "metadata", - validator: await validator.getAddress(), - data: packValidatorParams(query), - }); - - const zkProof = packZKProof(inputs, pi_a, pi_b, pi_c); - const [signer] = await ethers.getSigners(); - - const crossChainProofs = packCrossChainProofs( - await buildCrossChainProofs( - [globalStateMessage, identityStateMessage1, identityStateUpdate2], - signer, - ), - ); - - const tx = await verifier.submitZKPResponseV2( - [ - { - requestId: 0, - zkProof: zkProof, - data: metadatas, - }, - ], - crossChainProofs, - ); - - const txRes = await tx.wait(); - - const storageFields = [ - { - name: "userID", - value: 1n, - }, - { - name: "issuerID", - value: 2n, - }, - ]; - await checkStorageFields(verifier, 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 eventBeforeProofSubmitV2 = interfaceEventBeforeProofSubmitV2.decodeEventLog( - "BeforeProofSubmitV2", - 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); - - const interfaceEventAfterProofSubmitV2 = new ethers.Interface([ - "event AfterProofSubmitV2(tuple(uint64 requestId,bytes zkProof,bytes data)[])", - ]); - const eventAfterProofSubmitV2 = interfaceEventAfterProofSubmitV2.decodeEventLog( - "AfterProofSubmitV2", - 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); - - 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("2.0.2-mock"); - expect(proofStatus.blockNumber).to.be.equal(txRes.blockNumber); - expect(proofStatus.blockTimestamp).to.be.equal(txResTimestamp); - }); - - it("test getZKPRequest and request id exists", async () => { - const requestsCount = 3; - for (let i = 0; i < requestsCount; i++) { - await verifier.setZKPRequest(i, { - metadata: "metadataN" + i, - validator: await validator.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); - expect(request.metadata).to.be.equal("metadataN" + i); - await expect(verifier.getZKPRequest(i + 1)).to.be.rejectedWith("request id doesn't exist"); - } - const count = await verifier.getZKPRequestsCount(); - expect(count).to.be.equal(requestsCount); - }); -}); diff --git a/test/verifier/requestDisableable.test.ts b/test/verifier/requestDisableable.test.ts new file mode 100644 index 00000000..89b7a148 --- /dev/null +++ b/test/verifier/requestDisableable.test.ts @@ -0,0 +1,69 @@ +import { ethers } from "hardhat"; +import { beforeEach } from "mocha"; +import { DeployHelper } from "../../helpers/DeployHelper"; +import { expect } from "chai"; + +describe("RequestDisableable tests", function () { + let verifier, validator: any; + let request, paramsFromValidator: any; + + async function deployContractsFixture() { + const deployHelper = await DeployHelper.initialize(null, true); + const verifier = await ethers.deployContract("RequestDisableableTestWrapper", []); + + const { state } = await deployHelper.deployStateWithLibraries([], "Groth16VerifierStub"); + await verifier.initialize(await state.getAddress()); + + const validator = await ethers.deployContract("RequestValidatorStub"); + return { verifier, validator }; + } + + beforeEach(async function () { + ({ verifier, validator } = await deployContractsFixture()); + + request = { + requestId: 1, + metadata: "0x", + validator: await validator.getAddress(), + params: "0x", + }; + + paramsFromValidator = [ + { name: "groupID", value: 0 }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 0 }, + ]; + }); + + it("disable/enable request and onlyEnabledRequest modifier", async function () { + await validator.stub_setRequestParams([request.params], [paramsFromValidator]); + + await verifier.setRequests([request]); + + let isRequestEnabled = await verifier.isRequestEnabled(request.requestId); + expect(isRequestEnabled).to.be.true; + + await expect(verifier.testModifier(request.requestId)).not.to.be.reverted; + await expect(verifier.getRequestIfCanBeVerified(request.requestId)).not.to.be.reverted; + + await verifier.disableRequest(request.requestId); + + isRequestEnabled = await verifier.isRequestEnabled(request.requestId); + expect(isRequestEnabled).to.be.false; + + await expect(verifier.testModifier(request.requestId)) + .to.be.revertedWithCustomError(verifier, "RequestIsDisabled") + .withArgs(request.requestId); + + await expect(verifier.getRequestIfCanBeVerified(request.requestId)) + .to.be.revertedWithCustomError(verifier, "RequestIsDisabled") + .withArgs(request.requestId); + + await verifier.enableRequest(request.requestId); + + isRequestEnabled = await verifier.isRequestEnabled(request.requestId); + expect(isRequestEnabled).to.be.true; + + await expect(verifier.testModifier(request.requestId)).not.to.be.reverted; + }); +}); diff --git a/test/verifier/requestOwnership.test.ts b/test/verifier/requestOwnership.test.ts new file mode 100644 index 00000000..79d20cd8 --- /dev/null +++ b/test/verifier/requestOwnership.test.ts @@ -0,0 +1,53 @@ +import { ethers } from "hardhat"; +import { beforeEach } from "mocha"; +import { DeployHelper } from "../../helpers/DeployHelper"; +import { expect } from "chai"; + +describe("RequestOwnership tests", function () { + let verifier, validator: any; + let request, paramsFromValidator: any; + let signer1, signer2: any; + + async function deployContractsFixture() { + [signer1, signer2] = await ethers.getSigners(); + + const deployHelper = await DeployHelper.initialize(null, true); + const verifier = await ethers.deployContract("RequestOwnershipTestWrapper", []); + + const { state } = await deployHelper.deployStateWithLibraries([], "Groth16VerifierStub"); + await verifier.initialize(await state.getAddress()); + + const validator = await ethers.deployContract("RequestValidatorStub"); + return { verifier, validator, signer1, signer2 }; + } + + beforeEach(async function () { + ({ verifier, validator, signer1, signer2 } = await deployContractsFixture()); + + request = { + requestId: 1, + metadata: "0x", + validator: await validator.getAddress(), + params: "0x", + }; + + paramsFromValidator = [ + { name: "groupID", value: 0 }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 0 }, + ]; + }); + + it("setRequestOwner: change request ownership", async function () { + await validator.stub_setRequestParams([request.params], [paramsFromValidator]); + await verifier.setRequests([request]); + + let owner = await verifier.getRequestOwner(request.requestId); + expect(owner).to.be.equal(await signer1.getAddress()); + + await verifier.setRequestOwner(request.requestId, await signer2.getAddress()); + + owner = await verifier.getRequestOwner(request.requestId); + expect(owner).to.be.equal(await signer2.getAddress()); + }); +}); 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 a4117be9..00000000 --- a/test/verifier/universal-verifier-submit-V2.test.ts +++ /dev/null @@ -1,234 +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 submitZKPResponseV2 SigV2 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, - }; - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - const zkProof = packZKProof(inputs, pi_a, pi_b, pi_c); - const metadatas = "0x"; - const data = packValidatorParams(query); - - const requestIds = [0, 1, 2]; - const nonExistingRequestId = 3; - - const singleProof = [ - { - requestId: 0, - zkProof: zkProof, - data: metadatas, - }, - ]; - - const multiProof = [ - { - requestId: 1, - zkProof: zkProof, - data: metadatas, - }, - { - requestId: 2, - zkProof: zkProof, - data: metadatas, - }, - ]; - - let crossChainProofs; - - 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(); - - for (const requestId of requestIds) { - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await sig.getAddress(), - data: data, - }); - } - } - - const storageFields = [ - { - name: "userID", - value: 1n, - }, - { - name: "issuerID", - value: 2n, - }, - ]; - - async function checkStorageFields(verifier: any, requestId: number, storageFields: any[]) { - for (const field of storageFields) { - const value = await verifier.getProofStorageField( - await signer.getAddress(), - requestId, - field.name, - ); - expect(value).to.be.equal(field.value); - } - } - - beforeEach(async () => { - await loadFixture(deployContractsFixture); - crossChainProofs = packCrossChainProofs( - await buildCrossChainProofs( - [globalStateMessage, identityStateMessage1, identityStateUpdate2], - signer, - ), - ); - }); - - it("Test submit response V2", async () => { - const requestId = 0; - const tx = await verifier.submitZKPResponseV2(singleProof, crossChainProofs); - - const txRes = await tx.wait(); - await checkStorageFields(verifier, requestId, storageFields); - 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", - ); - - const requestIdsMulti = requestIds.slice(1, 3); - const txMulti = await verifier.submitZKPResponseV2(multiProof, crossChainProofs); - - const txResMulti = await txMulti.wait(); - - const eventsMulti = await verifier.queryFilter(filter, txRes.blockNumber + 1); - expect(eventsMulti[0].eventName).to.be.equal("ZKPResponseSubmitted"); - expect(eventsMulti[0].args.requestId).to.be.equal(1); - expect(eventsMulti[0].args.caller).to.be.equal(signerAddress); - - const { timestamp: txResTimestampMuti } = (await ethers.provider.getBlock( - txResMulti.blockNumber, - )) as Block; - - for (const requestId of requestIdsMulti) { - 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(txResMulti.blockNumber); - expect(status.blockTimestamp).to.be.equal(txResTimestampMuti); - await checkStorageFields(verifier, requestId, storageFields); - } - }); - - it("Test submit response V2 with disable/enable functionality", async () => { - await verifier.disableZKPRequest(0); - await expect(verifier.submitZKPResponseV2(singleProof, crossChainProofs)).to.be.rejectedWith( - "Request is disabled", - ); - - await verifier.disableZKPRequest(1); - await expect(verifier.submitZKPResponseV2(multiProof, crossChainProofs)).to.be.rejectedWith( - "Request is disabled", - ); - - await verifier.enableZKPRequest(0); - await expect(verifier.submitZKPResponseV2(singleProof, crossChainProofs)).not.to.be.rejected; - - await verifier.enableZKPRequest(1); - await expect(verifier.submitZKPResponseV2(multiProof, crossChainProofs)).not.to.be.rejected; - }); - - it("Test submit response V2 check whitelisted functionality", async () => { - await verifier.removeValidatorFromWhitelist(await sig.getAddress()); - await expect(verifier.submitZKPResponseV2(singleProof, crossChainProofs)).to.be.rejectedWith( - "Validator is not whitelisted", - ); - await expect(verifier.submitZKPResponseV2(multiProof, crossChainProofs)).to.be.rejectedWith( - "Validator is not whitelisted", - ); - - await verifier.addValidatorToWhitelist(await sig.getAddress()); - await expect(verifier.submitZKPResponseV2(singleProof, crossChainProofs)).not.to.be.rejected; - await expect(verifier.submitZKPResponseV2(multiProof, crossChainProofs)).not.to.be.rejected; - }); -}); diff --git a/test/verifier/universal-verifier.events.test.ts b/test/verifier/universal-verifier.events.test.ts deleted file mode 100644 index fa5441e9..00000000 --- a/test/verifier/universal-verifier.events.test.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { expect } from "chai"; -import { DeployHelper } from "../../helpers/DeployHelper"; -import { ethers } from "hardhat"; -import { packValidatorParams } from "../utils/validator-pack-utils"; -import { AbiCoder } from "ethers"; -import { CircuitId } from "@0xpolygonid/js-sdk"; - -describe("Universal Verifier events", function () { - let verifier: any, sig: any; - let signer; - - const queries = [ - { - schema: 111n, - claimPathKey: 8566939875427719562376598811066985304309117528846759529734201066483458512800n, - operator: 1n, - slotIndex: 0n, - value: [1420070400000000000n, ...new Array(63).fill("0").map((x) => BigInt(x))], - queryHash: BigInt( - "1496222740463292783938163206931059379817846775593932664024082849882751356658", - ), - circuitIds: [CircuitId.AtomicQuerySigV2OnChain], - skipClaimRevocationCheck: false, - claimPathNotExists: 0n, - }, - { - schema: BigInt("222"), - claimPathKey: BigInt( - "8566939875427719562376598811066985304309117528846759529734201066483458512800", - ), - operator: 1n, - slotIndex: 0n, - value: [1420070400000000000n, ...new Array(63).fill("0").map((x) => BigInt(x))], - queryHash: BigInt( - "1496222740463292783938163206931059379817846775593932664024082849882751356658", - ), - circuitIds: [CircuitId.AtomicQuerySigV2OnChain], - skipClaimRevocationCheck: true, - claimPathNotExists: 0n, - }, - { - schema: 333n, - claimPathKey: BigInt( - "8566939875427719562376598811066985304309117528846759529734201066483458512800", - ), - operator: 1n, - slotIndex: 0n, - value: [1420070400000000000n, ...new Array(63).fill("0").map((x) => BigInt(x))], - queryHash: BigInt( - "1496222740463292783938163206931059379817846775593932664024082849882751356658", - ), - circuitIds: [CircuitId.AtomicQuerySigV2OnChain], - skipClaimRevocationCheck: false, - claimPathNotExists: 0n, - }, - ]; - - const encodedDataAbi = [ - { - components: [ - { name: "schema", type: "uint256" }, - { name: "claimPathKey", type: "uint256" }, - { name: "operator", type: "uint256" }, - { name: "slotIndex", type: "uint256" }, - { name: "value", type: "uint256[]" }, - { name: "queryHash", type: "uint256" }, - { name: "allowedIssuers", type: "uint256[]" }, - { name: "circuitIds", type: "string[]" }, - { name: "skipClaimRevocationCheck", type: "bool" }, - { name: "claimPathNotExists", type: "uint256" }, - ], - name: "", - type: "tuple", - }, - ]; - - beforeEach(async () => { - [signer] = await ethers.getSigners(); - - const deployHelper = await DeployHelper.initialize(null, true); - const { 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( - "sigV2", - await state.getAddress(), - ); - sig = contracts.validator; - await verifier.addValidatorToWhitelist(await sig.getAddress()); - await verifier.connect(); - }); - - it("Check ZKPRequestSet event", async () => { - const requestsCount = 3; - const data = [ - 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], - }); - } - const filter = verifier.filters.ZKPRequestSet(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); - expect(decodedData.schema).to.equal(queries[index].schema); - expect(decodedData.claimPathKey).to.equal(queries[index].claimPathKey); - expect(decodedData.operator).to.equal(queries[index].operator); - expect(decodedData.slotIndex).to.equal(queries[index].slotIndex); - decodedData.value.forEach((v, i) => { - expect(v).to.equal(queries[index].value[i]); - }); - expect(decodedData.queryHash).to.equal(queries[index].queryHash); - decodedData.circuitIds.forEach((circuitId, i) => { - expect(circuitId).to.equal(queries[index].circuitIds[i]); - }); - expect(decodedData.skipClaimRevocationCheck).to.equal( - queries[index].skipClaimRevocationCheck, - ); - expect(decodedData.claimPathNotExists).to.equal(queries[index].claimPathNotExists); - }); - }); - - it("Check ZKPRequestUpdate 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.updateZKPRequest(0, { - metadata: "metadataN1", - validator: await sig.getAddress(), - data: updatedRequestData, - }); - - const filter = verifier.filters.ZKPRequestUpdate(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); - expect(decodedData.schema).to.equal(queries[1].schema); - expect(decodedData.claimPathKey).to.equal(queries[1].claimPathKey); - expect(decodedData.operator).to.equal(queries[1].operator); - expect(decodedData.slotIndex).to.equal(queries[1].slotIndex); - decodedData.value.forEach((v, i) => { - expect(v).to.equal(queries[1].value[i]); - }); - expect(decodedData.queryHash).to.equal(queries[1].queryHash); - decodedData.circuitIds.forEach((circuitId, i) => { - expect(circuitId).to.equal(queries[1].circuitIds[i]); - }); - expect(decodedData.skipClaimRevocationCheck).to.equal(queries[1].skipClaimRevocationCheck); - expect(decodedData.claimPathNotExists).to.equal(queries[1].claimPathNotExists); - }); - }); -}); diff --git a/test/verifier/universal-verifier.test.ts b/test/verifier/universal-verifier.test.ts index 4918f494..9fddda8b 100644 --- a/test/verifier/universal-verifier.test.ts +++ b/test/verifier/universal-verifier.test.ts @@ -2,411 +2,657 @@ 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 } from "ethers"; -import proofJson from "../validators/mtp/data/valid_mtp_user_genesis.json"; -import { CircuitId } from "@0xpolygonid/js-sdk"; +import { AbiCoder, Block } from "ethers"; +import { byteEncoder, CircuitId } from "@0xpolygonid/js-sdk"; import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; +import { contractsInfo } from "../../helpers/constants"; -describe("Universal Verifier MTP & SIG validators", function () { - let verifier: any, validator: any, state: any; +describe("Universal Verifier tests", function () { + let request, paramsFromValidator, multiRequest, authResponse, response: any; + let verifier: any, validator: any, authValidator: any, state: any; let signer, signer2, signer3; let signerAddress: string; let deployHelper: DeployHelper; - 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, - }; + const storageFields = [ + { + name: "userID", + value: 1n, + }, + { + name: "issuerID", + value: 2n, + }, + ]; + + const crossChainProofs = "0x"; async function deployContractsFixture() { const [ethSigner, ethSigner2, ethSigner3] = await ethers.getSigners(); deployHelper = await DeployHelper.initialize(null, true); const { state: stateContract } = await deployHelper.deployStateWithLibraries(["0x0112"]); - const verifierLib = await deployHelper.deployVerifierLib(); - const verifier: any = await deployHelper.deployUniversalVerifier( + const validator = await deployHelper.deployValidatorStub("RequestValidatorStub"); + await validator.stub_setVerifyResults([ + { name: "userID", value: 1 }, + { name: "issuerID", value: 2 }, + ]); + + const universalVerifier: any = await deployHelper.deployUniversalVerifier( ethSigner, await stateContract.getAddress(), - await verifierLib.getAddress(), ); - const validator = await deployHelper.deployValidatorStub(); - await verifier.addValidatorToWhitelist(await validator.getAddress()); - await verifier.connect(); + await universalVerifier.addValidatorToWhitelist(await validator.getAddress()); + await universalVerifier.connect(); + + const authValidator = await deployHelper.deployValidatorStub("AuthValidatorStub"); + await authValidator.stub_setVerifyResults(1); + + const authMethod = { + authMethod: "stubAuth", + validator: await authValidator.getAddress(), + params: "0x", + }; + await universalVerifier.setAuthMethod(authMethod); - return { ethSigner, ethSigner2, ethSigner3, stateContract, verifier, validator }; + return { + ethSigner, + ethSigner2, + ethSigner3, + stateContract, + universalVerifier, + validator, + authValidator, + }; } - 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.getResponseFieldValue( requestId, + await signer.getAddress(), field.name, ); expect(value).to.be.equal(field.value); } } - beforeEach(async () => { - ({ - ethSigner: signer, - ethSigner2: signer2, - ethSigner3: signer3, - stateContract: state, - verifier, - validator, - } = await loadFixture(deployContractsFixture)); - signerAddress = await signer.getAddress(); - }); + describe("Methods", function () { + beforeEach(async () => { + ({ + ethSigner: signer, + ethSigner2: signer2, + ethSigner3: signer3, + stateContract: state, + universalVerifier: verifier, + validator: validator, + } = await loadFixture(deployContractsFixture)); + request = { + requestId: 0, + metadata: "0x", + validator: await validator.getAddress(), + params: "0x", + }; + + authResponse = { + authMethod: "stubAuth", + proof: "0x", + }; + response = { + requestId: 0, + proof: "0x", + metadata: "0x", + }; + paramsFromValidator = [ + { name: "groupID", value: 0 }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 0 }, + ]; + multiRequest = { + multiRequestId: 1, + requestIds: [request.requestId], + groupIds: [], + metadata: "0x", + }; + await validator.stub_setRequestParams([request.params], [paramsFromValidator]); + + signerAddress = await signer.getAddress(); + }); - it("Test get state address", async () => { - const stateAddr = await verifier.getStateAddress(); - expect(stateAddr).to.be.equal(await state.getAddress()); - }); + it("Test get version", async () => { + const version = await verifier.version(); + expect(version).to.be.equal(contractsInfo.UNIVERSAL_VERIFIER.version); + }); - it("Test add, get ZKPRequest, requestIdExists, getZKPRequestsCount", async () => { - const requestsCount = 3; - const validatorAddr = await validator.getAddress(); + it("Test get state address", async () => { + let stateAddr = await verifier.getStateAddress(); + expect(stateAddr).to.be.equal(await state.getAddress()); - for (let i = 0; i < requestsCount; i++) { - await expect( - verifier.setZKPRequest(i, { - metadata: "metadataN" + i, - validator: validatorAddr, - data: "0x0" + i, - }), - ) - .to.emit(verifier, "ZKPRequestSet") - .withArgs(i, signerAddress, "metadataN" + i, validatorAddr, "0x0" + i); - const request = await verifier.getZKPRequest(i); - expect(request.metadata).to.be.equal("metadataN" + i); - expect(request.validator).to.be.equal(validatorAddr); - expect(request.data).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 verifier.setState(await signer.getAddress()); - const count = await verifier.getZKPRequestsCount(); - expect(count).to.be.equal(requestsCount); - }); + stateAddr = await verifier.getStateAddress(); + expect(stateAddr).to.be.equal(await signer.getAddress()); - it("Test add, get ZKPRequest, requestIdExists, getZKPRequestsCount with multiple set", async () => { - const requestsCount = 3; - const validatorAddr = await validator.getAddress(); - - const requestIds: number[] = []; - const requests: any[] = []; - for (let i = 0; i < requestsCount; i++) { - requestIds.push(i); - requests.push({ - metadata: "metadataN" + i, - validator: validatorAddr, - data: "0x0" + i, - }); - } + await verifier.setState(await state.getAddress()); + }); - await expect(verifier.setZKPRequests(requestIds, requests)) - .to.emit(verifier, "ZKPRequestSet") - .withArgs(0, signerAddress, "metadataN" + 0, validatorAddr, "0x0" + 0); + it("Test add, getRequest, requestIdExists, getRequestsCount", async () => { + const requestsCount = 3; + for (let i = 0; i < requestsCount; i++) { + request.requestId = i; + request.metadata = "metadataN" + i; + request.params = "0x0" + i; + + await validator.stub_setRequestParams([request.params], [paramsFromValidator]); + + await expect(verifier.setRequests([request])) + .to.emit(verifier, "RequestSet") + .withArgs(i, signerAddress, "metadataN" + i, request.validator, "0x0" + i); + const requestFromContract = await verifier.getRequest(i); + expect(requestFromContract.metadata).to.be.equal("metadataN" + i); + expect(requestFromContract.validator).to.be.equal(request.validator); + expect(requestFromContract.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.getRequest(i + 1)) + .to.be.revertedWithCustomError(verifier, "RequestIdNotFound") + .withArgs(i + 1); + } + + const count = await verifier.getRequestsCount(); + expect(count).to.be.equal(requestsCount); + }); - for (let i = 1; i < requestsCount; i++) { - const request = await verifier.getZKPRequest(i); - expect(request.metadata).to.be.equal("metadataN" + i); - expect(request.validator).to.be.equal(validatorAddr); - expect(request.data).to.be.equal("0x0" + i); + it("Test submit response single request", async () => { + const nonExistingRequestId = 1; + await verifier.setRequests([request]); - const requestIdExists = await verifier.requestIdExists(i); - expect(requestIdExists).to.be.true; - } + const tx = await verifier.submitResponse(authResponse, [response], crossChainProofs); - const count = await verifier.getZKPRequestsCount(); - expect(count).to.be.equal(requestsCount); - }); + const txRes = await tx.wait(); + await checkStorageFields(verifier, BigInt(request.requestId), storageFields); - it("Test submit response", async () => { - const requestId = 0; - const nonExistingRequestId = 1; - const data = packValidatorParams(query); + let filter = verifier.filters.ResponseSubmitted; + let events = await verifier.queryFilter(filter, -1); + expect(events[0].eventName).to.be.equal("ResponseSubmitted"); + expect(events[0].args.requestId).to.be.equal(request.requestId); + expect(events[0].args.caller).to.be.equal(signerAddress); - await verifier.setZKPRequest(0, { - metadata: "metadata", - validator: await validator.getAddress(), - data: data, + filter = verifier.filters.AuthResponseSubmitted; + events = await verifier.queryFilter(filter, -1); + expect(events[0].eventName).to.be.equal("AuthResponseSubmitted"); + expect(events[0].args.authMethod.hash).to.be.equal( + ethers.keccak256(byteEncoder.encode(authResponse.authMethod)), + ); + expect(events[0].args.caller).to.be.equal(signerAddress); + + const { timestamp: txResTimestamp } = (await ethers.provider.getBlock( + txRes.blockNumber, + )) as Block; + + const status = await verifier.getRequestProofStatus(signerAddress, request.requestId); + expect(status.isVerified).to.be.true; + expect(status.validatorVersion).to.be.equal("1.0.0-stub"); + expect(status.timestamp).to.be.equal(txResTimestamp); + + await expect(verifier.getRequestProofStatus(signerAddress, nonExistingRequestId)) + .to.be.revertedWithCustomError(verifier, "RequestIdNotFound") + .withArgs(nonExistingRequestId); }); - 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(); - const storageFields = [ - { - name: "userID", - value: inputs[1], - }, - { - name: "issuerID", - value: inputs[2], - }, - ]; - await checkStorageFields(verifier, requestId, storageFields); - 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; - - 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.blockTimestamp).to.be.equal(txResTimestamp); - - await expect(verifier.getProofStatus(signerAddress, nonExistingRequestId)).to.be.rejectedWith( - "request id doesn't exist", - ); - }); + it("Test submit response multiple request", async () => { + const requestIds = [1, 2]; + await verifier.setRequests([ + { + ...request, + requestId: requestIds[0], + }, + { + ...request, + requestId: requestIds[1], + }, + ]); + + const tx = await verifier.submitResponse( + authResponse, + [ + { + ...response, + requestId: requestIds[0], + }, + { + ...response, + requestId: requestIds[1], + }, + ], + crossChainProofs, + ); - it("Check access control", async () => { - const owner = signer; - const requestOwner = signer2; - const someSigner = signer3; - const requestId = 0; - const nonExistentRequestId = 1; - const requestOwnerAddr = await requestOwner.getAddress(); - const someSignerAddress = await someSigner.getAddress(); - - await expect(verifier.getRequestOwner(requestId)).to.be.rejectedWith( - "request id doesn't exist", - ); - await verifier.connect(requestOwner).setZKPRequest(requestId, { - metadata: "metadata", - validator: await validator.getAddress(), - data: packValidatorParams(query), + const txRes = await tx.wait(); + + for (const requestId of requestIds) { + await checkStorageFields(verifier, BigInt(requestId), storageFields); + } + + let filter = verifier.filters.ResponseSubmitted; + let events = await verifier.queryFilter(filter, -1); + expect(events[0].eventName).to.be.equal("ResponseSubmitted"); + expect(events[0].args.requestId).to.be.equal(requestIds[0]); + expect(events[0].args.caller).to.be.equal(signerAddress); + + filter = verifier.filters.AuthResponseSubmitted; + events = await verifier.queryFilter(filter, -1); + expect(events[0].eventName).to.be.equal("AuthResponseSubmitted"); + expect(events[0].args.authMethod.hash).to.be.equal( + ethers.keccak256(byteEncoder.encode(authResponse.authMethod)), + ); + 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.getRequestProofStatus(signerAddress, requestId); + expect(status.isVerified).to.be.true; + expect(status.validatorVersion).to.be.equal("1.0.0-stub"); + expect(status.timestamp).to.be.equal(txResTimestamp); + } }); - expect(await verifier.getRequestOwner(requestId)).to.be.equal(requestOwnerAddr); - await expect( - verifier.connect(someSigner).setRequestOwner(requestId, someSigner), - ).to.be.rejectedWith("Not an owner or request owner"); + it("Check access control", async () => { + const owner = signer; + const requestOwner = signer2; + const someSigner = signer3; + const nonExistentRequestId = 1; + const requestOwnerAddr = await requestOwner.getAddress(); + const someSignerAddress = await someSigner.getAddress(); - await verifier.connect(requestOwner).setRequestOwner(requestId, someSigner); - expect(await verifier.getRequestOwner(requestId)).to.be.equal(someSignerAddress); + await expect(verifier.getRequestOwner(request.requestId)) + .to.be.revertedWithCustomError(verifier, "RequestIdNotFound") + .withArgs(request.requestId); - await expect( - verifier.connect(requestOwner).setRequestOwner(requestId, requestOwnerAddr), - ).to.be.rejectedWith("Not an owner or request owner"); - await verifier.connect(owner).setRequestOwner(requestId, requestOwnerAddr); - expect(await verifier.getRequestOwner(requestId)).to.be.equal(requestOwnerAddr); + await verifier.connect(requestOwner).setRequests([request]); - await expect(verifier.getRequestOwner(nonExistentRequestId)).to.be.rejectedWith( - "request id doesn't exist", - ); - await expect( - verifier.setRequestOwner(nonExistentRequestId, someSignerAddress), - ).to.be.rejectedWith("request id doesn't exist"); - }); + expect(await verifier.getRequestOwner(request.requestId)).to.be.equal(requestOwnerAddr); + await expect(verifier.connect(someSigner).setRequestOwner(request.requestId, someSigner)) + .to.be.revertedWithCustomError(verifier, "NotAnOwnerOrRequestOwner") + .withArgs(someSigner); - it("Check disable/enable functionality", async () => { - const owner = signer; - const requestOwner = signer2; - const someSigner = signer3; - const requestId = 0; - const nonExistentRequestId = 1; + await verifier.connect(requestOwner).setRequestOwner(request.requestId, someSigner); + expect(await verifier.getRequestOwner(request.requestId)).to.be.equal(someSignerAddress); - await expect(verifier.isZKPRequestEnabled(requestId)).to.be.rejectedWith( - "request id doesn't exist", - ); + await expect( + verifier.connect(requestOwner).setRequestOwner(request.requestId, requestOwnerAddr), + ) + .to.be.revertedWithCustomError(verifier, "NotAnOwnerOrRequestOwner") + .withArgs(requestOwner); + + await verifier.connect(owner).setRequestOwner(request.requestId, requestOwnerAddr); + expect(await verifier.getRequestOwner(request.requestId)).to.be.equal(requestOwnerAddr); + + await expect(verifier.getRequestOwner(nonExistentRequestId)) + .to.be.revertedWithCustomError(verifier, "RequestIdNotFound") + .withArgs(nonExistentRequestId); + await expect(verifier.setRequestOwner(nonExistentRequestId, someSignerAddress)) + .to.be.revertedWithCustomError(verifier, "RequestIdNotFound") + .withArgs(nonExistentRequestId); + }); - await verifier.connect(requestOwner).setZKPRequest(requestId, { - metadata: "metadata", - validator: await validator.getAddress(), - data: packValidatorParams(query), + it("Test submit response with disable/enable functionality", async () => { + const requestIds = [0, 1, 2]; + + const singleResponse = [response]; + + const multiResponses = [ + { + ...response, + requestId: 1, + }, + { + ...response, + requestId: 2, + }, + ]; + + for (const requestId of requestIds) { + await verifier.setRequests([ + { + ...request, + requestId: requestId, + }, + ]); + } + + await verifier.disableRequest(singleResponse[0].requestId); + await expect(verifier.submitResponse(authResponse, singleResponse, crossChainProofs)) + .to.be.revertedWithCustomError(verifier, "RequestIsDisabled") + .withArgs(singleResponse[0].requestId); + + await verifier.disableRequest(multiResponses[0].requestId); + await expect(verifier.submitResponse(authResponse, multiResponses, crossChainProofs)) + .to.be.revertedWithCustomError(verifier, "RequestIsDisabled") + .withArgs(multiResponses[0].requestId); + + await verifier.enableRequest(singleResponse[0].requestId); + await expect(verifier.submitResponse(authResponse, singleResponse, crossChainProofs)).not.to + .be.rejected; + + await verifier.enableRequest(multiResponses[0].requestId); + await expect(verifier.submitResponse(authResponse, multiResponses, crossChainProofs)).not.to + .be.rejected; }); - expect(await verifier.isZKPRequestEnabled(requestId)).to.be.true; - await expect(verifier.connect(someSigner).disableZKPRequest(requestId)).to.be.rejectedWith( - "Not an owner or request owner", - ); - expect(await verifier.isZKPRequestEnabled(requestId)).to.be.true; + it("Test submit response check whitelisted functionality", async () => { + const requestIds = [0, 1, 2]; + const singleResponse = [response]; + + const multiResponses = [ + { + ...response, + requestId: 1, + }, + { + ...response, + requestId: 2, + }, + ]; + + for (const requestId of requestIds) { + await verifier.setRequests([ + { + ...request, + requestId: requestId, + }, + ]); + } + + await verifier.removeValidatorFromWhitelist(await validator.getAddress()); + await expect(verifier.submitResponse(authResponse, singleResponse, crossChainProofs)) + .to.be.revertedWithCustomError(verifier, "ValidatorIsNotWhitelisted") + .withArgs(await validator.getAddress()); + await expect(verifier.submitResponse(authResponse, multiResponses, crossChainProofs)) + .to.be.revertedWithCustomError(verifier, "ValidatorIsNotWhitelisted") + .withArgs(await validator.getAddress()); + + await verifier.addValidatorToWhitelist(await validator.getAddress()); + await expect(verifier.submitResponse(authResponse, singleResponse, crossChainProofs)).not.to + .be.rejected; + await expect(verifier.submitResponse(authResponse, multiResponses, crossChainProofs)).not.to + .be.rejected; + }); - await verifier.connect(owner).disableZKPRequest(requestId); - expect(await verifier.isZKPRequestEnabled(requestId)).to.be.false; + it("Check updateRequest", async () => { + const owner = signer; + const requestOwner = signer2; + const requestId = 0; - await expect(verifier.connect(someSigner).enableZKPRequest(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).setRequests([request]); - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - await verifier.submitZKPResponse(0, inputs, pi_a, pi_b, pi_c); + let requestStored = await verifier.getRequest(requestId); + expect(requestStored.metadata).to.be.equal(request.metadata); + await expect( + verifier.connect(requestOwner).updateRequest(request), + ).to.be.revertedWithCustomError(verifier, "OwnableUnauthorizedAccount"); - await verifier.connect(requestOwner).disableZKPRequest(requestId); - await expect(verifier.submitZKPResponse(0, inputs, pi_a, pi_b, pi_c)).to.be.rejectedWith( - "Request is disabled", - ); - await expect( - verifier.verifyZKPResponse( - 0, - inputs, - pi_a, - pi_b, - pi_c, - "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", - ), - ).to.be.rejectedWith("Request is disabled"); - - await expect(verifier.isZKPRequestEnabled(nonExistentRequestId)).to.be.rejectedWith( - "request id doesn't exist", - ); - await expect(verifier.disableZKPRequest(nonExistentRequestId)).to.be.rejectedWith( - "request id doesn't exist", - ); - await expect(verifier.enableZKPRequest(nonExistentRequestId)).to.be.rejectedWith( - "request id doesn't exist", - ); + await verifier.connect(owner).updateRequest({ + ...request, + metadata: "metadata2", + }); + + requestStored = await verifier.getRequest(requestId); + expect(requestStored.metadata).to.be.equal("metadata2"); + }); + + it("updateRequest - not existed request", async () => { + const owner = signer; + const requestId = 0; + + await expect(verifier.connect(owner).updateRequest(request)) + .to.be.revertedWithCustomError(verifier, "RequestIdNotFound") + .withArgs(requestId); + }); + + it("Test set request fails with VerifierIDIsNotValid", async () => { + ({ + ethSigner: signer, + ethSigner2: signer2, + stateContract: state, + validator: validator, + universalVerifier: verifier, + } = await loadFixture(deployContractsFixture)); + + paramsFromValidator = [ + { name: "groupID", value: 0 }, + { name: "verifierID", value: 2 }, + { name: "nullifierSessionID", value: 0 }, + ]; + + const requestId = 40; + + const request = { + requestId: requestId, + metadata: "0x", + validator: await validator.getAddress(), + params: "0x", + }; + await validator.stub_setRequestParams([request.params], [paramsFromValidator]); + + await verifier.setVerifierID(1); + + await expect(verifier.setRequests([request])) + .to.be.revertedWithCustomError(verifier, "VerifierIDIsNotValid") + .withArgs(2, 1); + }); }); - it("Check whitelisted validators", async () => { - const owner = signer; - const someAddress = signer2; - const requestId = 1; - const otherRequestId = 2; - const { validator: mtp } = await deployHelper.deployValidatorContractsWithVerifiers( - "mtpV2", - await state.getAddress(), - ); - 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"); - - await expect(verifier.connect(someAddress).addValidatorToWhitelist(mtpValAddr)) - .to.be.revertedWithCustomError(verifier, "OwnableUnauthorizedAccount") - .withArgs(someAddress); - expect(await verifier.isWhitelistedValidator(mtpValAddr)).to.be.false; - - await verifier.connect(owner).addValidatorToWhitelist(mtpValAddr); - expect(await verifier.isWhitelistedValidator(mtpValAddr)).to.be.true; - - await expect( - verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: mtpValAddr, - data: "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"); - - await verifier.removeValidatorFromWhitelist(mtpValAddr); - - await expect( - verifier.submitZKPResponse( - requestId, - [], - [0, 0], - [ - [0, 0], - [0, 0], + describe("Events", function () { + const queries = [ + { + schema: 111n, + claimPathKey: 8566939875427719562376598811066985304309117528846759529734201066483458512800n, + operator: 1n, + slotIndex: 0n, + value: [1420070400000000000n, ...new Array(63).fill("0").map((x) => BigInt(x))], + queryHash: BigInt( + "1496222740463292783938163206931059379817846775593932664024082849882751356658", + ), + circuitIds: [CircuitId.AtomicQuerySigV2OnChain], + skipClaimRevocationCheck: false, + claimPathNotExists: 0n, + }, + { + schema: 222n, + claimPathKey: BigInt( + "8566939875427719562376598811066985304309117528846759529734201066483458512800", + ), + operator: 1n, + slotIndex: 0n, + value: [1420070400000000000n, ...new Array(63).fill("0").map((x) => BigInt(x))], + queryHash: BigInt( + "1496222740463292783938163206931059379817846775593932664024082849882751356658", + ), + circuitIds: [CircuitId.AtomicQuerySigV2OnChain], + skipClaimRevocationCheck: true, + claimPathNotExists: 0n, + }, + { + schema: 333n, + claimPathKey: BigInt( + "8566939875427719562376598811066985304309117528846759529734201066483458512800", + ), + operator: 1n, + slotIndex: 0n, + value: [1420070400000000000n, ...new Array(63).fill("0").map((x) => BigInt(x))], + queryHash: BigInt( + "1496222740463292783938163206931059379817846775593932664024082849882751356658", + ), + circuitIds: [CircuitId.AtomicQuerySigV2OnChain], + skipClaimRevocationCheck: false, + claimPathNotExists: 0n, + }, + ]; + + const encodedDataAbi = [ + { + components: [ + { name: "schema", type: "uint256" }, + { name: "claimPathKey", type: "uint256" }, + { name: "operator", type: "uint256" }, + { name: "slotIndex", type: "uint256" }, + { name: "value", type: "uint256[]" }, + { name: "queryHash", type: "uint256" }, + { name: "allowedIssuers", type: "uint256[]" }, + { name: "circuitIds", type: "string[]" }, + { name: "skipClaimRevocationCheck", type: "bool" }, + { name: "claimPathNotExists", type: "uint256" }, ], - [0, 0], - ), - ).to.be.rejectedWith("Validator is not whitelisted"); - }); + name: "", + type: "tuple", + }, + ]; - it("Check updateZKPRequest", async () => { - const owner = signer; - const requestOwner = signer2; - const requestId = 0; - const data = packValidatorParams(query); + beforeEach(async () => { + ({ + ethSigner: signer, + ethSigner2: signer2, + ethSigner3: signer3, + stateContract: state, + universalVerifier: verifier, + validator: validator, + authValidator: authValidator, + } = await loadFixture(deployContractsFixture)); + signerAddress = await signer.getAddress(); + }); - await verifier.connect(requestOwner).setZKPRequest(requestId, { - metadata: "metadata", - validator: await validator.getAddress(), - data: data, + it("Check RequestSet event", async () => { + const requestsCount = 3; + const params = [ + packValidatorParams(queries[0]), + packValidatorParams(queries[1]), + packValidatorParams(queries[2]), + ]; + + paramsFromValidator = [ + { name: "groupID", value: 0 }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 0 }, + ]; + + for (let i = 0; i < requestsCount; i++) { + await validator.stub_setRequestParams([params[i]], [paramsFromValidator]); + await expect( + verifier.setRequests([ + { + ...request, + requestId: i, + params: params[i], + }, + ]), + ).to.emit(verifier, "RequestSet"); + } + 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.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); + expect(decodedData.slotIndex).to.equal(queries[index].slotIndex); + decodedData.value.forEach((v, i) => { + expect(v).to.equal(queries[index].value[i]); + }); + expect(decodedData.queryHash).to.equal(queries[index].queryHash); + decodedData.circuitIds.forEach((circuitId, i) => { + expect(circuitId).to.equal(queries[index].circuitIds[i]); + }); + expect(decodedData.skipClaimRevocationCheck).to.equal( + queries[index].skipClaimRevocationCheck, + ); + expect(decodedData.claimPathNotExists).to.equal(queries[index].claimPathNotExists); + }); }); - let request = await verifier.getZKPRequest(requestId); - expect(request.metadata).to.be.equal("metadata"); + it("Check RequestUpdate event", async () => { + const originalRequestData = packValidatorParams(queries[0]); + const updatedRequestData = packValidatorParams(queries[1]); - await expect( - verifier.connect(requestOwner).updateZKPRequest(requestId, { - metadata: "metadata", - validator: await validator.getAddress(), - data: data, - }), - ).to.be.revertedWithCustomError(verifier, "OwnableUnauthorizedAccount"); - - await verifier.connect(owner).updateZKPRequest(requestId, { - metadata: "metadata2", - validator: await validator.getAddress(), - data: data, + await validator.stub_setRequestParams([originalRequestData], [paramsFromValidator]); + await validator.stub_setRequestParams([updatedRequestData], [paramsFromValidator]); + + await verifier.setRequests([ + { + ...request, + params: originalRequestData, + }, + ]); + + await verifier.updateRequest({ + ...request, + metadata: "metadataN1", + params: updatedRequestData, + }); + + 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.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); + expect(decodedData.slotIndex).to.equal(queries[1].slotIndex); + decodedData.value.forEach((v, i) => { + expect(v).to.equal(queries[1].value[i]); + }); + expect(decodedData.queryHash).to.equal(queries[1].queryHash); + decodedData.circuitIds.forEach((circuitId, i) => { + expect(circuitId).to.equal(queries[1].circuitIds[i]); + }); + expect(decodedData.skipClaimRevocationCheck).to.equal(queries[1].skipClaimRevocationCheck); + expect(decodedData.claimPathNotExists).to.equal(queries[1].claimPathNotExists); + }); }); - request = await verifier.getZKPRequest(requestId); - expect(request.metadata).to.be.equal("metadata2"); - }); + it("Check AuthMethodSet event", async () => { + const nonExistingAuthMethod = { + authMethod: "stubAuth2", + validator: await authValidator.getAddress(), + params: "0x", + }; + const tx = await verifier.setAuthMethod(nonExistingAuthMethod); + + const filter = verifier.filters.AuthMethodSet; + const events = await verifier.queryFilter(filter, tx.blockNumber); + expect(events[0].eventName).to.be.equal("AuthMethodSet"); + expect(events[0].args.authMethod.hash).to.be.equal( + ethers.keccak256(byteEncoder.encode(nonExistingAuthMethod.authMethod)), + ); + expect(events[0].args.validator).to.be.equal(nonExistingAuthMethod.validator); + expect(events[0].args.params).to.be.equal(nonExistingAuthMethod.params); + }); - it("updateZKPRequest - not existed request", async () => { - const owner = signer; - const requestId = 0; - const data = packValidatorParams(query); + it("Check MultiRequestSet event", async function () { + await validator.stub_setRequestParams([request.params], [paramsFromValidator]); + await verifier.setRequests([request]); - await expect( - verifier.connect(owner).updateZKPRequest(requestId, { - metadata: "metadata", - validator: await validator.getAddress(), - data: data, - }), - ).to.be.rejectedWith("equest id doesn't exis"); + await expect(verifier.setMultiRequest(multiRequest)).to.emit(verifier, "MultiRequestSet"); + + const filter = verifier.filters.MultiRequestSet; + const events = await verifier.queryFilter(filter, -1); + expect(events[0].eventName).to.be.equal("MultiRequestSet"); + expect(events[0].args.multiRequestId).to.be.equal(multiRequest.multiRequestId); + expect(events[0].args.requestIds).to.deep.equal(multiRequest.requestIds); + expect(events[0].args.groupIds).to.deep.equal(multiRequest.groupIds); + }); }); }); diff --git a/test/verifier/universal-verifier.v3.test.ts b/test/verifier/universal-verifier.v3.test.ts deleted file mode 100644 index 08c840c5..00000000 --- a/test/verifier/universal-verifier.v3.test.ts +++ /dev/null @@ -1,312 +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 { calculateQueryHashV3 } from "../utils/query-hash-utils"; -import { expect } from "chai"; -import { CircuitId } from "@0xpolygonid/js-sdk"; -import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; -import proofJson from "../validators/v3/data/valid_bjj_user_genesis_auth_disabled_v3.json"; -import stateTransition1 from "../validators/common-data/issuer_from_genesis_state_to_first_auth_disabled_transition_v3.json"; -import stateTransition11 from "../validators/common-data/issuer_from_genesis_state_to_first_transition_v3.json"; -import stateTransition12 from "../validators/common-data/user_from_genesis_state_to_first_transition_v3.json"; -import stateTransition13 from "../validators/common-data/issuer_from_first_state_to_second_transition_v3.json"; -import { packZKProof } from "../utils/packData"; -import { TEN_YEARS } from "../../helpers/constants"; - -const storageFields = [ - { - name: "issuerID", - value: 22057981499787921734624217749308316644136637822444794206796063681866502657n, - }, - { - name: "userID", - value: 23013175891893363078841232968022302880776034013620341061794940968520126978n, - }, - { name: "timestamp", value: 1642074362n }, - { - name: "linkID", - value: 19823993270096139446564592922993947503208333537792611306066620392561342309875n, - }, - { name: "nullifier", value: 0 }, -]; - -describe("Universal Verifier V3 validator", function () { - let verifier: any, v3Validator: any, state: any; - let signer, signer2; - let deployHelper: DeployHelper; - - 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 query = { - schema, - claimPathKey, - operator, - slotIndex, - value, - circuitIds: [CircuitId.AtomicQueryV3OnChain], - skipClaimRevocationCheck: false, - queryHash, - groupID: 1, - nullifierSessionID: nullifierSessionId, // for ethereum based user - proofType: 1, // 1 for BJJ - verifierID: verifierId, - }; - - const initializeState = async () => { - deployHelper = await DeployHelper.initialize(null, true); - - const { state: stateContract } = await deployHelper.deployStateWithLibraries(["0x0212"]); - const verifierLib = await deployHelper.deployVerifierLib(); - const contracts = await deployHelper.deployValidatorContractsWithVerifiers( - "v3", - await stateContract.getAddress(), - ); - const validator = contracts.validator; - const universalVerifier: any = await deployHelper.deployUniversalVerifier( - signer, - await stateContract.getAddress(), - await verifierLib.getAddress(), - ); - await universalVerifier.addValidatorToWhitelist(await validator.getAddress()); - await universalVerifier.connect(); - - return { stateContract, validator, universalVerifier }; - }; - - async function deployContractsFixture() { - const [ethSigner, ethSigner2] = await ethers.getSigners(); - const { stateContract, validator, universalVerifier } = await initializeState(); - return { ethSigner, ethSigner2, stateContract, universalVerifier, validator }; - } - - async function checkStorageFields(verifier: any, requestId: number, storageFields: any[]) { - for (const field of storageFields) { - const value = await verifier.getProofStorageField( - await signer.getAddress(), - requestId, - field.name, - ); - expect(value).to.be.equal(field.value); - } - } - - before(async () => { - ({ - ethSigner: signer, - ethSigner2: signer2, - stateContract: state, - validator: v3Validator, - 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 expect(verifier.submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c)).not.to.be - .rejected; - - await checkStorageFields(verifier, requestId, storageFields); - }); - - it("Test submit response V2", async () => { - const requestId = 32; - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - - const zkProof = packZKProof(inputs, pi_a, pi_b, pi_c); - - const crossChainProofs = "0x"; - - const metadatas = "0x"; - - await expect( - verifier.submitZKPResponseV2( - [ - { - requestId, - zkProof: zkProof, - data: metadatas, - }, - ], - crossChainProofs, - ), - ).not.to.be.rejected; - - await checkStorageFields(verifier, 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; - await expect( - verifier.connect(signer2).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), - ).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 requestId = 33; - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, - }); - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - - await expect( - verifier.connect(signer).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), - ).to.be.rejectedWith("Issuer is not on the Allowed Issuers list"); - }); - - it("Test submit response fails with Invalid Link ID pub signal", async () => { - const query2 = { - ...query, - }; - query2.groupID = 0; - const requestId = 34; - const data = packV3ValidatorParams(query2); - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, - }); - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - - await expect( - verifier.connect(signer).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), - ).to.be.rejectedWith("Invalid Link ID pub signal"); - }); - - it("Test submit response fails with Proof type should match the requested one in query", async () => { - const query2 = { - ...query, - }; - query2.proofType = 2; - const requestId = 35; - const data = packV3ValidatorParams(query2); - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, - }); - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - - await expect( - verifier.connect(signer).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), - ).to.be.rejectedWith("Proof type should match the requested one in query"); - }); - - it("Test submit response fails with Invalid nullify pub signal", async () => { - const query2 = { - ...query, - }; - query2.nullifierSessionID = "2"; - const requestId = 36; - const data = packV3ValidatorParams(query2); - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, - }); - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - - await expect( - verifier.connect(signer).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), - ).to.be.rejectedWith("Invalid nullify pub signal"); - }); - - it("Test submit response fails with Query hash does not match the requested one", async () => { - const query2 = { - ...query, - }; - query2.queryHash = BigInt(0); - const requestId = 37; - const data = packV3ValidatorParams(query2); - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, - }); - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - - await expect( - verifier.connect(signer).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), - ).to.be.rejectedWith("Query hash does not match the requested one"); - }); - - it("Test submit response fails with Generated proof is outdated", async () => { - ({ - ethSigner: signer, - ethSigner2: signer2, - stateContract: state, - validator: v3Validator, - universalVerifier: verifier, - } = await loadFixture(deployContractsFixture)); - - await publishState(state, stateTransition11 as any); - await publishState(state, stateTransition12 as any); - await publishState(state, stateTransition13 as any); - - const data = packV3ValidatorParams(query); - const requestId = 37; - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, - }); - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - - await expect( - verifier.connect(signer).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), - ).to.be.rejectedWith("Generated proof is outdated"); - }); -}); diff --git a/test/verifier/validatorWhitelist.test.ts b/test/verifier/validatorWhitelist.test.ts new file mode 100644 index 00000000..1eef3957 --- /dev/null +++ b/test/verifier/validatorWhitelist.test.ts @@ -0,0 +1,75 @@ +import { ethers } from "hardhat"; +import { beforeEach } from "mocha"; +import { DeployHelper } from "../../helpers/DeployHelper"; +import { expect } from "chai"; + +describe("ValidatorWhitelist tests", function () { + let verifier, validator: any; + let signer1, signer2: any; + let request, paramsFromValidator: any; + + async function deployContractsFixture() { + [signer1, signer2] = await ethers.getSigners(); + + const deployHelper = await DeployHelper.initialize(null, true); + const verifier = await ethers.deployContract("ValidatorWhitelistTestWrapper", []); + + const { state } = await deployHelper.deployStateWithLibraries([], "Groth16VerifierStub"); + await verifier.initialize(await state.getAddress()); + + const validator = await ethers.deployContract("RequestValidatorStub"); + return { verifier, validator, signer1, signer2 }; + } + + beforeEach(async function () { + ({ verifier, validator, signer1, signer2 } = await deployContractsFixture()); + + request = { + requestId: 1, + metadata: "0x", + validator: await validator.getAddress(), + params: "0x", + }; + + paramsFromValidator = [ + { name: "groupID", value: 0 }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 0 }, + ]; + }); + + it("whitelist/remove Validators and modifier onlyWhitelistedValidator", async function () { + let isWhitelistedValidator = await verifier.isWhitelistedValidator( + await validator.getAddress(), + ); + expect(isWhitelistedValidator).to.be.false; + + await expect(verifier.testModifier(await validator.getAddress())).to.be.revertedWithCustomError( + verifier, + "ValidatorIsNotWhitelisted", + ); + + await verifier.addValidatorToWhitelist(await validator.getAddress()); + + await validator.stub_setRequestParams([request.params], [paramsFromValidator]); + await verifier.setRequests([request]); + + await expect(verifier.testModifier(await validator.getAddress())).not.to.be.reverted; + await expect(verifier.getRequestIfCanBeVerified(request.requestId)).not.to.be.reverted; + + isWhitelistedValidator = await verifier.isWhitelistedValidator(await validator.getAddress()); + expect(isWhitelistedValidator).to.be.true; + + await verifier.removeValidatorFromWhitelist(await validator.getAddress()); + + await expect(verifier.testModifier(await validator.getAddress())) + .to.be.revertedWithCustomError(verifier, "ValidatorIsNotWhitelisted") + .withArgs(await validator.getAddress()); + await expect(verifier.getRequestIfCanBeVerified(request.requestId)) + .to.be.revertedWithCustomError(verifier, "ValidatorIsNotWhitelisted") + .withArgs(await validator.getAddress()); + + isWhitelistedValidator = await verifier.isWhitelistedValidator(await validator.getAddress()); + expect(isWhitelistedValidator).to.be.false; + }); +}); diff --git a/test/verifier/verifer.test.ts b/test/verifier/verifer.test.ts new file mode 100644 index 00000000..6fff36aa --- /dev/null +++ b/test/verifier/verifer.test.ts @@ -0,0 +1,660 @@ +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, validator1, validator2: any; + let request, paramsFromValidator, authMethod: 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 verifier = await ethers.deployContract("VerifierTestWrapper", []); + + const { state } = await deployHelper.deployStateWithLibraries([], "Groth16VerifierStub"); + await verifier.initialize(await state.getAddress()); + + const authValidatorStub = await ethers.deployContract("AuthValidatorStub"); + await authValidatorStub.stub_setVerifyResults(1); + + authMethod = { + authMethod: "stubAuth", + validator: await authValidatorStub.getAddress(), + params: "0x", + }; + + await verifier.setAuthMethod(authMethod); + + const validator1 = await ethers.deployContract("RequestValidatorStub"); + const validator2 = await ethers.deployContract("RequestValidatorStub"); + return { verifier, validator1, validator2 }; + } + + describe("Single request tests", function () { + beforeEach(async function () { + [sender] = await ethers.getSigners(); + ({ verifier, validator1, validator2 } = await deployContractsFixture()); + + verifierId = await verifier.getVerifierID(); + + request = { + requestId: 1, + metadata: "0x", + validator: await validator1.getAddress(), + params: "0x", + }; + + paramsFromValidator = [ + { name: "groupID", value: 0 }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 0 }, + ]; + + multiRequest = { + multiRequestId: 1, + requestIds: [request.requestId], + groupIds: [], + metadata: "0x", + }; + }); + + it("setRequests: should not exist when creating", async function () { + await validator1.stub_setRequestParams([request.params], [paramsFromValidator]); + + let requestIdExists = await verifier.requestIdExists(request.requestId); + expect(requestIdExists).to.be.false; + let requestsCount = await verifier.getRequestsCount(); + expect(requestsCount).to.be.equal(0); + + await expect(verifier.setRequests([request])).not.to.be.rejected; + await expect(verifier.setRequests([request])) + .to.be.revertedWithCustomError(verifier, "RequestIdAlreadyExists") + .withArgs(request.requestId); + + requestIdExists = await verifier.requestIdExists(request.requestId); + expect(requestIdExists).to.be.true; + requestsCount = await verifier.getRequestsCount(); + expect(requestsCount).to.be.equal(1); + }); + + it("setRequests: nullifierSessionID may be not unique if EQUAL to 0", async function () { + await validator1.stub_setRequestParams([request.params], [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 = [ + { name: "groupID", value: 0 }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 1 }, + ]; + await validator1.stub_setRequestParams([request.params], [paramsFromValidator]); + + await verifier.setRequests([request]); + request.requestId = 2; + await expect(verifier.setRequests([request])) + .to.be.revertedWithCustomError(verifier, "NullifierSessionIDAlreadyExists") + .withArgs(1); + }); + + it("setRequests: requestId should be valid", async function () { + await validator1.stub_setRequestParams([request.params], [paramsFromValidator]); + + request.requestId = BigInt( + "0x0002000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + ); // requestId without valid prefix 0x00000000000000_00 or 0x00000000000000_01 (eigth byte) + + await expect(verifier.setRequests([request])).to.be.revertedWithCustomError( + verifier, + "RequestIdTypeNotValid", + ); + + request.requestId = BigInt( + "0x0000000001000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + ); // requestId uses reserved bytes (firt to seventh byte) 0x00000000000000 + await expect(verifier.setRequests([request])).to.be.revertedWithCustomError( + verifier, + "RequestIdUsesReservedBytes", + ); + + request.requestId = BigInt( + "0x0001000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + ); // requestId idType is valid but calculation from hash params is not valid + await expect(verifier.setRequests([request])).to.be.revertedWithCustomError( + verifier, + "RequestIdNotValid", + ); + + request.requestId = + (BigInt(ethers.keccak256(request.params)) & + BigInt("0x0000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")) + + BigInt("0x0001000000000000000000000000000000000000000000000000000000000000"); // requestId is valid; + await expect(verifier.setRequests([request])).not.to.be.rejected; + }); + + 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 = [ + { name: "groupID", value: 1 }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 0 }, + ]; + await validator1.stub_setRequestParams([groupRequest1.params], [paramsFromValidator]); + await validator1.stub_setRequestParams([groupRequest2.params], [paramsFromValidator]); + + let groupExists = await verifier.groupIdExists(groupID); + expect(groupExists).to.be.false; + let groupsCount = await verifier.getGroupsCount(); + expect(groupsCount).to.be.equal(0); + + await verifier.setRequests([groupRequest1, groupRequest2]); + + groupExists = await verifier.groupIdExists(groupID); + expect(groupExists).to.be.true; + 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].requestId).to.be.equal(groupRequest1.requestId); + expect(groupedRequests[1].requestId).to.be.equal(groupRequest2.requestId); + }); + + it("setRequests: a group should not exist previously", async function () { + const groupID = 1; + const groupRequest1 = { ...request, groupID }; + const groupRequest2 = { ...request, requestId: 2, groupID }; + + paramsFromValidator = [ + { name: "groupID", value: 1 }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 0 }, + ]; + await validator1.stub_setRequestParams([groupRequest1.params], [paramsFromValidator]); + await validator1.stub_setRequestParams([groupRequest2.params], [paramsFromValidator]); + + await verifier.setRequests([groupRequest1, groupRequest2]); + + await expect(verifier.setRequests([groupRequest1, groupRequest2])) + .to.be.revertedWithCustomError(verifier, "GroupIdAlreadyExists") + .withArgs(groupID); + }); + + it("getRequest: requestId should exist", async function () { + await expect(verifier.getRequest(request.requestId)) + .to.be.revertedWithCustomError(verifier, "RequestIdNotFound") + .withArgs(request.requestId); + + paramsFromValidator = [ + { name: "groupID", value: 0 }, + { name: "verifierID", value: verifierId }, + { name: "nullifierSessionID", value: 0 }, + ]; + + await validator1.stub_setRequestParams([request.params], [paramsFromValidator]); + await verifier.setRequests([request]); + + const 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()); + }); + + it("getRequestProofStatus: requestId should exist", async function () { + const nonExistingRequestId = 2; + + await expect(verifier.getRequestProofStatus(signerAddress, nonExistingRequestId)) + .to.be.revertedWithCustomError(verifier, "RequestIdNotFound") + .withArgs(nonExistingRequestId); + }); + + it("getAuthMethod: authMethod should exist", async function () { + const authMethod2 = { ...authMethod, authMethod: "stubAuth2" }; + + await expect(verifier.getAuthMethod(authMethod2.authMethod)) + .to.be.revertedWithCustomError(verifier, "AuthMethodNotFound") + .withArgs(authMethod2.authMethod); + + await expect(verifier.setAuthMethod(authMethod)) + .to.be.revertedWithCustomError(verifier, "AuthMethodAlreadyExists") + .withArgs(authMethod.authMethod); + + await expect(verifier.setAuthMethod(authMethod2)).not.to.be.reverted; + + const authMethodObject = await verifier.getAuthMethod(authMethod.authMethod); + expect(authMethodObject.validator).to.be.equal(authMethod2.validator); + expect(authMethodObject.params).to.be.equal(authMethod2.params); + }); + + it("enableAuthMethod/disableAuthMethod", async function () { + let authMethodObject = await verifier.getAuthMethod(authMethod.authMethod); + expect(authMethodObject.isActive).to.be.true; + + await verifier.disableAuthMethod(authMethod.authMethod); + + authMethodObject = await verifier.getAuthMethod(authMethod.authMethod); + expect(authMethodObject.isActive).to.be.false; + + await verifier.enableAuthMethod(authMethod.authMethod); + + authMethodObject = await verifier.getAuthMethod(authMethod.authMethod); + expect(authMethodObject.isActive).to.be.true; + }); + + it("submitResponse: not repeated responseFields from validator", async function () { + await validator1.stub_setRequestParams([request.params], [paramsFromValidator]); + await verifier.setRequests([request]); + await validator1.stub_setVerifyResults([ + { + name: "someFieldName1", + value: 1, + }, + { + name: "someFieldName2", + value: 2, + }, + ]); + + const authResponse = { + authMethod: "stubAuth", + proof: "0x", + }; + const response = { + requestId: request.requestId, + proof: "0x", + metadata: "0x", + }; + const crossChainProofs = "0x"; + + let isRequestProofVerified = await verifier.isRequestProofVerified(sender, request.requestId); + expect(isRequestProofVerified).to.be.false; + + await verifier.submitResponse(authResponse, [response], crossChainProofs); + + isRequestProofVerified = await verifier.isRequestProofVerified(sender, request.requestId); + expect(isRequestProofVerified).to.be.true; + + 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); + + const responseFields = await verifier.getResponseFields(request.requestId, sender); + expect(responseFields.length).to.be.equal(2); + expect(responseFields[0].name).to.be.equal("someFieldName1"); + expect(responseFields[0].value).to.be.equal(1); + expect(responseFields[1].name).to.be.equal("someFieldName2"); + expect(responseFields[1].value).to.be.equal(2); + }); + + it("submitResponse: should throw if repeated responseFields from validator", async function () { + await validator1.stub_setRequestParams([request.params], [paramsFromValidator]); + await verifier.setRequests([request]); + await validator1.stub_setVerifyResults([ + { + name: "someFieldName1", + value: 1, + }, + { + name: "someFieldName1", + value: 1, + }, + ]); + + const authResponse = { + authMethod: "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("submitResponse: userID in response fields should match auth userID", async function () { + await validator1.stub_setRequestParams([request.params], [paramsFromValidator]); + await verifier.setRequests([request]); + + let userID = 1; // we assume that userID is hardcoded to 1 in the auth stub contract + await validator1.stub_setVerifyResults([ + { + name: "userID", + value: userID, + }, + ]); + + const authResponse = { + authMethod: "stubAuth", + proof: "0x", + }; + const response = { + requestId: request.requestId, + proof: "0x", + metadata: "0x", + }; + const crossChainProofs = "0x"; + + await verifier.submitResponse(authResponse, [response], crossChainProofs); + + userID = 2; + await validator1.stub_setVerifyResults([ + { + name: "userID", + value: userID, + }, + ]); + + await expect(verifier.submitResponse(authResponse, [response], crossChainProofs)) + .to.revertedWithCustomError(verifier, "UserIDMismatch") + .withArgs(1, 2); + }); + }); + + describe("Multi request tests", function () { + before(async function () { + [sender] = await ethers.getSigners(); + ({ verifier, validator1, validator2 } = await deployContractsFixture()); + + request = { + requestId: 1, + metadata: "0x", + validator: await validator1.getAddress(), + params: "0x", + }; + + paramsFromValidator = [ + { name: "groupID", value: 0 }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 0 }, + ]; + + multiRequest = { + multiRequestId: 1, + requestIds: [request.requestId], + groupIds: [], + metadata: "0x", + }; + }); + + it("setMultiRequest: should not exist when creating", async function () { + await validator1.stub_setRequestParams([request.params], [paramsFromValidator]); + await verifier.setRequests([request]); + + let multiRequestIdExists = await verifier.multiRequestIdExists(multiRequest.multiRequestId); + expect(multiRequestIdExists).to.be.false; + await expect(verifier.setMultiRequest(multiRequest)).not.to.be.rejected; + await expect(verifier.setMultiRequest(multiRequest)) + .revertedWithCustomError(verifier, "MultiRequestIdAlreadyExists") + .withArgs(multiRequest.multiRequestId); + multiRequestIdExists = await verifier.multiRequestIdExists(multiRequest.multiRequestId); + expect(multiRequestIdExists).to.be.true; + }); + + it("setMultiRequest: requestIds and groupIds should exist", async function () { + const multiRequest2 = { + multiRequestId: 2, + requestIds: [2], + groupIds: [], + metadata: "0x", + }; + + await expect(verifier.setMultiRequest(multiRequest2)) + .revertedWithCustomError(verifier, "RequestIdNotFound") + .withArgs(multiRequest2.requestIds[0]); + + const multiRequest3 = { + multiRequestId: 3, + requestIds: [], + groupIds: [2], + metadata: "0x", + }; + + await expect(verifier.setMultiRequest(multiRequest3)) + .revertedWithCustomError(verifier, "GroupIdNotFound") + .withArgs(multiRequest3.groupIds[0]); + }); + + it("getMultiRequest: multiRequestId should exist", async function () { + const nonExistingMultiRequestId = 5; + await expect(verifier.getMultiRequest(nonExistingMultiRequestId)) + .to.be.revertedWithCustomError(verifier, "MultiRequestIdNotFound") + .withArgs(nonExistingMultiRequestId); + const multiRequestObject = await verifier.getMultiRequest(multiRequest.multiRequestId); + expect(multiRequestObject.multiRequestId).to.be.equal(multiRequest.multiRequestId); + expect(multiRequestObject.metadata).to.be.equal(multiRequest.metadata); + expect(multiRequestObject.requestIds.length).to.be.equal(multiRequest.requestIds.length); + expect(multiRequestObject.groupIds.length).to.be.equal(multiRequest.groupIds.length); + }); + + 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 areMultiRequestProofsVerified = await verifier.areMultiRequestProofsVerified( + multiRequest.multiRequestId, + signerAddress, + ); + expect(areMultiRequestProofsVerified).to.be.false; + + let isMultiRequest2Verified = await verifier.areMultiRequestProofsVerified( + 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 validator1.stub_setVerifyResults([ + { + name: "userID", + value: userID, + }, + ]); + + const authResponse = { + authMethod: "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 + areMultiRequestProofsVerified = await verifier.areMultiRequestProofsVerified( + multiRequest.multiRequestId, + signerAddress, + ); + expect(areMultiRequestProofsVerified).to.be.true; + + isMultiRequest2Verified = await verifier.areMultiRequestProofsVerified( + multiRequest2.multiRequestId, + signerAddress, + ); + expect(isMultiRequest2Verified).to.be.true; + }); + + it("getMultiRequestProofsStatus: multi request should exist", async function () { + const nonExistingMultiRequestId = 5; + await expect(verifier.getMultiRequestProofsStatus(nonExistingMultiRequestId, signerAddress)) + .to.be.revertedWithCustomError(verifier, "MultiRequestIdNotFound") + .withArgs(nonExistingMultiRequestId); + await expect(verifier.getMultiRequestProofsStatus(multiRequest.multiRequestId, signerAddress)) + .not.to.be.rejected; + }); + + it("getMultiRequestProofsStatus: linkID should be equal to all requests in a group, otherwise multiRequest pointing to it returns false", async function () { + const groupID = 1; + const groupRequest1 = { ...request, requestId: 5, groupID }; + const groupRequest2 = { + ...request, + validator: await validator2.getAddress(), + requestId: 6, + groupID, + }; + paramsFromValidator = [ + { name: "groupID", value: groupID }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 0 }, + ]; + await validator1.stub_setRequestParams([groupRequest1.params], [paramsFromValidator]); + await validator2.stub_setRequestParams([groupRequest2.params], [paramsFromValidator]); + + await verifier.setRequests([groupRequest1, groupRequest2]); + + const multiRequest3 = { + multiRequestId: 3, + requestIds: [], + groupIds: [groupID], + metadata: "0x", + }; + await verifier.setMultiRequest(multiRequest3); + + const userID = 1; + await validator1.stub_setVerifyResults([ + { name: "userID", value: userID }, + { name: "issuerID", value: 2 }, + { name: "linkID", value: 3 }, + ]); + await validator2.stub_setVerifyResults([ + { name: "userID", value: userID }, + { name: "issuerID", value: 2 }, + { name: "linkID", value: 4 }, + ]); + + const authResponse = { + authMethod: "stubAuth", + proof: "0x", + }; + const response1 = { + requestId: groupRequest1.requestId, + proof: "0x", + metadata: "0x", + }; + const response2 = { + requestId: groupRequest2.requestId, + proof: "0x", + metadata: "0x", + }; + const crossChainProofs = "0x"; + + await verifier.submitResponse(authResponse, [response1, response2], crossChainProofs); + await expect( + verifier.getMultiRequestProofsStatus(multiRequest3.multiRequestId, signerAddress), + ).to.be.revertedWithCustomError(verifier, "LinkIDNotTheSameForGroupedRequests"); + + const areMultiRequestProofsVerified = await verifier.areMultiRequestProofsVerified( + multiRequest3.multiRequestId, + signerAddress, + ); + expect(areMultiRequestProofsVerified).to.be.false; + }); + + it("getMultiRequestProofsStatus: all request with same linkID in a group already verified returns true", async function () { + const groupID = 2; + const groupRequest1 = { ...request, requestId: 10, groupID }; + const groupRequest2 = { + ...request, + requestId: 11, + groupID, + }; + paramsFromValidator = [ + { name: "groupID", value: groupID }, + { name: "verifierID", value: 0 }, + { name: "nullifierSessionID", value: 0 }, + ]; + await validator1.stub_setRequestParams([groupRequest1.params], [paramsFromValidator]); + + await verifier.setRequests([groupRequest1, groupRequest2]); + + const multiRequest4 = { + multiRequestId: 4, + requestIds: [], + groupIds: [groupID], + metadata: "0x", + }; + await verifier.setMultiRequest(multiRequest4); + + const userID = 1; + await validator1.stub_setVerifyResults([ + { name: "userID", value: userID }, + { name: "issuerID", value: 2 }, + { name: "linkID", value: 3 }, + ]); + + const authResponse = { + authMethod: "stubAuth", + proof: "0x", + }; + const response1 = { + requestId: groupRequest1.requestId, + proof: "0x", + metadata: "0x", + }; + const response2 = { + requestId: groupRequest2.requestId, + proof: "0x", + metadata: "0x", + }; + const crossChainProofs = "0x"; + + // partial responses of the multiRequest group + await verifier.submitResponse(authResponse, [response1], crossChainProofs); + + await expect( + verifier.getMultiRequestProofsStatus(multiRequest4.multiRequestId, signerAddress), + ).to.be.revertedWithCustomError(verifier, "LinkIDNotTheSameForGroupedRequests"); + + let areMultiRequestProofsVerified = await verifier.areMultiRequestProofsVerified( + multiRequest4.multiRequestId, + signerAddress, + ); + expect(areMultiRequestProofsVerified).to.be.false; + + // all responses of the multiRequest group completed + await verifier.submitResponse(authResponse, [response2], crossChainProofs); + + await expect( + verifier.getMultiRequestProofsStatus(multiRequest4.multiRequestId, signerAddress), + ).not.to.be.rejected; + + areMultiRequestProofsVerified = await verifier.areMultiRequestProofsVerified( + multiRequest4.multiRequestId, + signerAddress, + ); + expect(areMultiRequestProofsVerified).to.be.true; + }); + }); +});