From 66fe34ebcd417514168285b43a66b5649013aa57 Mon Sep 17 00:00:00 2001 From: Andriian Chestnykh Date: Tue, 5 Nov 2024 13:23:14 +0000 Subject: [PATCH] WIP --- contracts/interfaces/IZKPVerifier.sol | 6 ++ contracts/lib/VerifierLibReqType1.sol | 52 ++++++++++ contracts/verifiers/UniversalVerifier.sol | 5 + contracts/verifiers/ZKPVerifierBase.sol | 117 +++++++++++++++++++++- 4 files changed, 177 insertions(+), 3 deletions(-) create mode 100644 contracts/lib/VerifierLibReqType1.sol diff --git a/contracts/interfaces/IZKPVerifier.sol b/contracts/interfaces/IZKPVerifier.sol index 585dd43c..f8ea37bd 100644 --- a/contracts/interfaces/IZKPVerifier.sol +++ b/contracts/interfaces/IZKPVerifier.sol @@ -45,6 +45,12 @@ interface IZKPVerifier { bytes data; } + struct ZKPResponseV3 { + uint256 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. diff --git a/contracts/lib/VerifierLibReqType1.sol b/contracts/lib/VerifierLibReqType1.sol new file mode 100644 index 00000000..91a784d0 --- /dev/null +++ b/contracts/lib/VerifierLibReqType1.sol @@ -0,0 +1,52 @@ +// 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 VerifierLibReqType1 { + function writeProofResults( + ZKPVerifierBase.ZKPVerifierStorageProofType1 storage self, + address sender, + uint256 requestId, + ICircuitValidator.Signal[] memory signals + ) public { + + // TODO COMPLETE THE METHOD IMPLEMENTATION + + ZKPVerifierBase.ProofReqType1[] storage proofs = self._proofs[sender][requestId]; + + uint256 issuerId; + for (uint256 i = 0; i < signals.length; i++) { + if (signals[i].name == "issuerId") { + issuerId = signals[i].value; + } + } + require(issuerId != 0, "Issuer ID not found in the signals"); + + // TODO research if pushing elements with mapping would work here + // without throwing exceptions in compilator + proofs.push(ProofReqType1{ + isVerified: true, + validatorVersion: self._requests[requestId].validator.version(), + blockTimestamp: block.timestamp + }); + + ZKPVerifierBase.ProofReqType1 storage proof = proofs[proofs.length - 1]; + + for (uint256 i = 0; i < signals.length; i++) { + proof.storageFields[signals[i].name] = signals[i].value; + if (signals[i].name == "issuerId") { + issuerId = signals[i].value; + } + } + + // TODO need to incapusulate this in a function maybe + // as we dong user proofs.length - 1 to avoid 1 proof position ambiguity + self._indexInProofs[sender][requestId][issuerId] = proofs.length; + } +} diff --git a/contracts/verifiers/UniversalVerifier.sol b/contracts/verifiers/UniversalVerifier.sol index 0af69d51..0e66f65e 100644 --- a/contracts/verifiers/UniversalVerifier.sol +++ b/contracts/verifiers/UniversalVerifier.sol @@ -25,6 +25,7 @@ contract UniversalVerifier is /// @dev Event emitted upon submitting a ZKP request event ZKPResponseSubmitted(uint64 indexed requestId, address indexed caller); + event ZKPResponseSubmittedReqType1(uint256 indexed requestId, address indexed caller, uint256 indexed issuerID); /// @dev Event emitted upon adding a ZKP request event ZKPRequestSet( @@ -65,6 +66,9 @@ contract UniversalVerifier is ) public override(RequestOwnership, ValidatorWhitelist, ZKPVerifierBase) { super.setZKPRequest(requestId, request); + //TODO should we define a separate event for RequestType1? + //or we can just decode issuer offchain? + //at least we need to upgrade requestId param in the event uint64 -> uint256 emit ZKPRequestSet( requestId, _msgSender(), @@ -102,6 +106,7 @@ contract UniversalVerifier is bytes memory crossChainProof ) public override { super.submitZKPResponseV2(responses, crossChainProof); + // TODO emit specific event depending the RequestType for (uint256 i = 0; i < responses.length; i++) { emit ZKPResponseSubmitted(responses[i].requestId, _msgSender()); } diff --git a/contracts/verifiers/ZKPVerifierBase.sol b/contracts/verifiers/ZKPVerifierBase.sol index 8d3f4d3c..b4e4cf92 100644 --- a/contracts/verifiers/ZKPVerifierBase.sol +++ b/contracts/verifiers/ZKPVerifierBase.sol @@ -26,12 +26,122 @@ abstract contract ZKPVerifierBase is IZKPVerifier, ContextUpgradeable { /// @custom:storage-location erc7201:iden3.storage.ZKPVerifier struct ZKPVerifierStorage { - mapping(address user => mapping(uint64 requestId => Proof)) _proofs; - mapping(uint64 requestId => IZKPVerifier.ZKPRequest) _requests; - uint64[] _requestIds; + // This group of field is gor RequestType=0 processing + mapping(address user => mapping(uint256 requestId => Proof)) _proofs; + // TODO research if changing from uint64 to uint256 would not break storage location + mapping(uint256 requestId => IZKPVerifier.ZKPRequest) _requests; + // TODO not forget to migrate from uint64[] to uint256[] in the contract upgrade tx + uint256[] _requestIds; IState _state; } + /// @custom:storage-location erc7201:iden3.storage.ZKPVerifier.ProofType1 + struct ZKPVerifierStorageProofType1 { + // This group of field is gor RequestType=1 processing + // RequestType=1 has a feature of issuer filtering + mapping(address user => mapping(uint256 requestId => ProofReqType1[])) _proofs; + // We should increase the index by 1 when writing to the mapping + // This is to avoid unimbiguous with 0 proof position in the _proof array + mapping(address user => mapping(uint256 requestId => mapping(uint256 issuerId => uint256 _indexInProofs))) _proofsByIssuers; + } + + struct ProofReqType1 { + bool isVerified; + mapping(string key => uint256 inputValue) storageFields; + string validatorVersion; + uint256 blockTimestamp; + mapping(string key => bytes) metadata; + } + + // 32 bytes (in Big Endian): 31-0x00(not used), 30-0x01(requestType), 29..8-0x00(not used), 7..0 requestId + function setZKPRequestV3( + uint256 requestId, + IZKPVerifier.ZKPRequest calldata request + ) public virtual checkRequestExistence(requestId, false) { + // TODO create the isEligibleRequestType method + require(hasEligibleRequestType(requestId), "RequestType not supported"); + + ZKPVerifierStorage storage s = _getZKPVerifierStorage(); + s._requests[requestId] = request; + s._requestIds.push(requestId); + } + + function getProofStatusV3( + address sender, + uint256 requestId, + bytes filterData, + ) public view checkRequestExistence(requestId, true) returns (IZKPVerifier.ProofStatus memory) { + uint8 requestType = _getRequestType(requestId); + + if (requestType == 0) { + require(filterData.length == 0, "FilterData not supported for RequestType=0"); + Proof storage proof = _getZKPVerifierStorage()._proofs[sender][requestId]; + return + IZKPVerifier.ProofStatus( + proof.isVerified, + proof.validatorVersion, + proof.blockNumber, + proof.blockTimestamp + ); + } else if (requestType == 1) { + uint256 issuerId = abi.decode(filterData, (uint256)); + require(issuerId != 0, "Issuer ID parameter required for RequestType=1"); + + // TODO complete by getting the the index first and then getting the proof from the mapping (incapsulate something maybe) + ProofReqType1 storage proof = _getZKPVerifierStorageProofType1()._proofs[sender][requestId][issuerId]; + + return + IZKPVerifier.ProofStatus( + proof.isVerified, + proof.validatorVersion, + 0, + proof.blockTimestamp + ); + } else { + revert("RequestType not supported"); + } + } + + function submitZKPResponseV3( + IZKPVerifier.ZKPResponseV3[] memory responses, + bytes memory crossChainProofs + ) { + for (uint256 i = 0; i < responses.length; i++) { + IZKPVerifier.ZKPResponseV3 memory response = responses[i]; + + address sender = _msgSender(); + + uint8 requestType = _getRequestType(response.requestId); + IZKPVerifier.ZKPRequest memory request = _getZKPVerifierStorage()._requests[response.requestId]; + + if (requestType == 0) { + ICircuitValidator.Signal[] memory signals = request.validator.verifyV2( + response.zkProof, + request.data, + sender, + _getZKPVerifierStorage()._state + ); + + _getZKPVerifierStorage().writeProofResultsV2(sender, response.requestId, signals); + } else if (requestType == 1) { + ICircuitValidator.Signal[] memory signals = request.validator.verifyV2( + response.zkProof, + request.data, + sender, + _getZKPVerifierStorage()._state + ); + + _getZKPVerifierStorageProofType1().writeProofResults(sender, response.requestId, signals); + } else { + revert("RequestType not supported"); + } + + if (response.data.length > 0) { + revert("Metadata not supported yet"); + } + } + } + // keccak256(abi.encode(uint256(keccak256("iden3.storage.ZKPVerifier")) - 1)) & ~bytes32(uint256(0xff)); bytes32 internal constant ZKPVerifierStorageLocation = 0x512d18c55869273fec77e70d8a8586e3fb133e90f1db24c6bcf4ff3506ef6a00; @@ -48,6 +158,7 @@ abstract contract ZKPVerifierBase is IZKPVerifier, ContextUpgradeable { } using VerifierLib for ZKPVerifierStorage; + using VerifierLibReqType1 for ZKPVerifierStorageProofType1; function __ZKPVerifierBase_init(IState state) internal onlyInitializing { __ZKPVerifierBase_init_unchained(state);