diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierAnonAadhaarV1.sol b/contracts/lib/groth16-verifiers/Groth16VerifierAnonAadhaarV1.sol new file mode 100644 index 00000000..38fb527d --- /dev/null +++ b/contracts/lib/groth16-verifiers/Groth16VerifierAnonAadhaarV1.sol @@ -0,0 +1,226 @@ +// 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.7.0 <0.9.0; + +contract Groth16VerifierAnonAadhaarV1 { + // 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 = 9752776248207923469867409382048716372612962508996673790438054623489759145780; + uint256 constant deltax2 = 9452966396337616788385483674588918865821745355579743187934167481883534967251; + uint256 constant deltay1 = 15208746120897712994584783416569201488530823308245332170362178262653051227153; + uint256 constant deltay2 = 1743063522514331149547203759617585666894492323061121432061356664097165019185; + + + uint256 constant IC0x = 17202926283251598065231527376648019777623479314208943009386780888674303306367; + uint256 constant IC0y = 275122378331887271415548857816654626631770508982339731839029501536657326428; + + uint256 constant IC1x = 6793065755890846769340018862424041607459712744183098712209475459344516081736; + uint256 constant IC1y = 9071617632382870191496047914286194606374346215985030020661707710600804984468; + + uint256 constant IC2x = 8036455770137753300143301227052694753324655839004232562996994892756513744632; + uint256 constant IC2y = 19136034516434561880116093412780628312815490220057647258792780777662444980579; + + uint256 constant IC3x = 5706924235985036010295393494457870846665277481967479020222798374799047750256; + uint256 constant IC3y = 16335939767066344430970552411852108062971274211549461481263778305792680100688; + + uint256 constant IC4x = 13362589808591082140154747717874938555566489737803726066109447908122332925248; + uint256 constant IC4y = 9533438191675367092873436171711161489331286873030419565001344655397871220342; + + uint256 constant IC5x = 13606256073504308111394692719682918711474465633647202003403244063200398917656; + uint256 constant IC5y = 14074028033425546109580220784064373333536645170395823067627266760390456677393; + + uint256 constant IC6x = 19096454032987284766595414848523440262913762053347052157186315602771633028839; + uint256 constant IC6y = 7276871217582302819620304900124070838673347672010981352688103755427813080463; + + uint256 constant IC7x = 18459111221168914001640566342970864469601586632412046593346422818505556097195; + uint256 constant IC7y = 13100409853915252749737243319212327344620678393375462514810315616945336813081; + + uint256 constant IC8x = 9200173373433970145496240930187071045143369120243275026153868648172499323668; + uint256 constant IC8y = 7200362604079959194735981300560157143070925411981289227281356488895967807761; + + uint256 constant IC9x = 14814413443159487223931208725152452842059865722271281787555352116049799452733; + uint256 constant IC9y = 17373462594478087004748496082618076921959076827177568046785097901984202939893; + + + // 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[9] 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))) + + + // -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))) + + + // Validate all evaluations + let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem) + + mstore(0, isValid) + return(0, 0x20) + } + } + } diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierAnonAadhaarV1Wrapper.sol b/contracts/lib/groth16-verifiers/Groth16VerifierAnonAadhaarV1Wrapper.sol new file mode 100644 index 00000000..c53bd7e3 --- /dev/null +++ b/contracts/lib/groth16-verifiers/Groth16VerifierAnonAadhaarV1Wrapper.sol @@ -0,0 +1,59 @@ +// +// 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.4 <0.9.0; + +import {Groth16VerifierAnonAadhaarV1} from "./Groth16VerifierAnonAadhaarV1.sol"; +import {IVerifier} from "../../interfaces/IVerifier.sol"; + +contract Groth16VerifierAnonAadhaarV1Wrapper is Groth16VerifierAnonAadhaarV1, IVerifier { + /** + * @dev Number of public signals for atomic mtp circuit + */ + uint256 constant PUBSIGNALS_LENGTH = 9; + + /** + * @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 valid. + */ + function verify( + uint256[2] calldata a, + uint256[2][2] calldata b, + uint256[2] calldata c, + uint256[] calldata input + ) public view returns (bool r) { + uint[PUBSIGNALS_LENGTH] memory pubSignals; + + require(input.length == PUBSIGNALS_LENGTH, "expected array length is 9"); + + for (uint256 i = 0; i < PUBSIGNALS_LENGTH; i++) { + pubSignals[i] = input[i]; + } + + return this.verifyProof(a, b, c, pubSignals); + } +} diff --git a/contracts/validators/AnonAadhaarV1Validator.sol b/contracts/validators/AnonAadhaarV1Validator.sol new file mode 100644 index 00000000..6e18e32f --- /dev/null +++ b/contracts/validators/AnonAadhaarV1Validator.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {IVerifier} from "../interfaces/IVerifier.sol"; +import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; +import {IState} from "../interfaces/IState.sol"; +import {CredentialAtomicQueryValidatorBase} from "./CredentialAtomicQueryValidatorBase.sol"; + +/** + * @dev AnonAadhaarV1Validator validator + */ +contract AnonAadhaarV1Validator is CredentialAtomicQueryValidatorBase { + struct PubSignals { + uint256 pubKeyHash; + uint256 nullifier; + uint256 claimRoot; + uint256 hashIndex; + uint256 hashValue; + uint256 nullifierSeed; + uint256 signalHash; + uint256 expirationDate; + uint256 templateRoot; + } + /** + * @dev Version of contract + */ + string public constant VERSION = "1.0.0"; + + string internal constant CIRCUIT_ID = "anonAadhaarV1"; + + /** + * @dev Initialize the contract + * @param _verifierContractAddr Address of the verifier contract + */ + function initialize( + address _verifierContractAddr, + address _stateContractAddr, + address owner + ) public initializer { + _setInputToIndex("pubKeyHash", 0); + _setInputToIndex("nullifier", 1); + _setInputToIndex("claimRoot", 2); + _setInputToIndex("hashIndex", 3); + _setInputToIndex("hashValue", 4); + _setInputToIndex("nullifierSeed", 5); + _setInputToIndex("signalHash", 6); + _setInputToIndex("expirationDate", 7); + _setInputToIndex("templateRoot", 8); + + _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({ + pubKeyHash: inputs[0], + nullifier: inputs[1], + claimRoot: inputs[2], + hashIndex: inputs[3], + hashValue: inputs[4], + nullifierSeed: inputs[5], + signalHash: inputs[6], + expirationDate: inputs[7], + templateRoot: inputs[8] + }); + + 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. + * @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); + _verifyZKP(inputs, a, b, c); + ICircuitValidator.Signal[] memory signals = new ICircuitValidator.Signal[](9); + signals[0] = ICircuitValidator.Signal({name: "pubKeyHash", value: pubSignals.pubKeyHash}); + signals[1] = ICircuitValidator.Signal({name: "nullifier", value: pubSignals.nullifier}); + signals[2] = ICircuitValidator.Signal({name: "claimRoot", value: pubSignals.claimRoot}); + signals[3] = ICircuitValidator.Signal({name: "hashIndex", value: pubSignals.hashIndex}); + signals[4] = ICircuitValidator.Signal({name: "hashValue", value: pubSignals.hashValue}); + signals[5] = ICircuitValidator.Signal({name: "nullifierSeed", value: pubSignals.nullifierSeed}); + signals[6] = ICircuitValidator.Signal({name: "signalHash", value: pubSignals.signalHash}); + signals[7] = ICircuitValidator.Signal({name: "expirationDate", value: pubSignals.expirationDate}); + signals[8] = ICircuitValidator.Signal({name: "templateRoot", value: pubSignals.templateRoot}); + 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/verifiers/AnonAadhaarCredentialIssuing.sol b/contracts/verifiers/AnonAadhaarCredentialIssuing.sol new file mode 100644 index 00000000..664e860a --- /dev/null +++ b/contracts/verifiers/AnonAadhaarCredentialIssuing.sol @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {IdentityLib} from "../lib/IdentityLib.sol"; +import {IZKPVerifier} from "../interfaces/IZKPVerifier.sol"; +import {EmbeddedZKPVerifier} from "./EmbeddedZKPVerifier.sol"; +import {IState} from "../interfaces/IState.sol"; +import {IdentityBase} from "../lib/IdentityBase.sol"; + +/** + * @dev Address ownership credential issuer. + * This issuer issue non-merklized credentials decentralized. + */ +contract AnonAadhaarCredentialIssuing is IdentityBase, EmbeddedZKPVerifier { + using IdentityLib for IdentityLib.Data; + + /// @custom:storage-location erc7201:polygonid.storage.AnonAadhaarCredentialIssuing + struct AnonAadhaarCredentialIssuingStorage { + uint256 nullifierSeed; + uint256 publicKeysHash; + mapping(uint256 => bool) nullifiers; + } + + // check if the hash was calculated correctly + // keccak256(abi.encode(uint256(keccak256("polygonid.storage.AnonAadhaarCredentialIssuing")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant AnonAadhaarCredentialIssuingStorageLocation = + 0x528fbd6ba0ce880481f220f86e7f05969027c6442ac6670247599a0f6783c100; + + function _getAnonAadhaarCredentialIssuingStorage() + private + pure + returns (AnonAadhaarCredentialIssuingStorage storage store) + { + assembly { + store.slot := AnonAadhaarCredentialIssuingStorageLocation + } + } + + function initialize( + uint256 nullifierSeed, + uint256 publicKeyHash, + address _stateContractAddr, + bytes2 idType + ) public initializer { + AnonAadhaarCredentialIssuingStorage storage $ = _getAnonAadhaarCredentialIssuingStorage(); + $.nullifierSeed = nullifierSeed; + $.publicKeysHash = publicKeyHash; + + super.initialize(_stateContractAddr, idType); + super.__EmbeddedZKPVerifier_init(_msgSender(), IState(_stateContractAddr)); + } + + function _validatePublicInputs( + uint256 hashIndex, + uint256 hashValue, + uint256 expirationDate, + uint256 nullifier, + uint256 pubKeyHash, + uint256 nullifierSeed + ) private view { + AnonAadhaarCredentialIssuingStorage storage $ = _getAnonAadhaarCredentialIssuingStorage(); + require(hashIndex != 0, "Invalid hashIndex"); + require(hashValue != 0, "Invalid hashValue"); + + require(nullifierSeed == $.nullifierSeed, "Invalid nullifierSeed"); + require(pubKeyHash == $.publicKeysHash, "Invalid pubKeyHash"); + + require(expirationDate > block.timestamp, "Credential is expired"); + + require(!$.nullifiers[nullifier], "Nullifier already exists"); + } + + function _addHashAndTransit(uint256 hi, uint256 hv) private { + _getIdentityBaseStorage().identity.addClaimHash(hi, hv); + _getIdentityBaseStorage().identity.transitState(); + } + + function _setNullifier(uint256 nullifier) private { + AnonAadhaarCredentialIssuingStorage storage $ = _getAnonAadhaarCredentialIssuingStorage(); + $.nullifiers[nullifier] = true; + } + + function _afterProofSubmitV2(IZKPVerifier.ZKPResponse[] memory responses) internal override { + require(responses.length == 1, "Only one response is allowed"); + uint256 hashIndex = super.getProofStorageField(_msgSender(), responses[0].requestId, "hashIndex"); + uint256 hashValue = super.getProofStorageField(_msgSender(), responses[0].requestId, "hashValue"); + uint256 expirationDate = super.getProofStorageField(_msgSender(), responses[0].requestId, "expirationDate"); + uint256 nullifier = super.getProofStorageField(_msgSender(), responses[0].requestId, "nullifier"); + uint256 pubKeyHash = super.getProofStorageField(_msgSender(), responses[0].requestId, "pubKeyHash"); + uint256 nullifierSeed = super.getProofStorageField(_msgSender(), responses[0].requestId, "nullifierSeed"); + + _validatePublicInputs(hashIndex, hashValue, expirationDate, nullifier, pubKeyHash, nullifierSeed); + _setNullifier(nullifier); + _addHashAndTransit(hashIndex, hashValue); + } +} + +/* +The code we need if we want to convert timestamps to the YYYY-MM-DD format and compare them + +function _compareDates(uint256 timestamp) internal view returns (bool) { + // Convert nanoseconds to seconds + uint256 secondsTimestamp = timestamp.div(1e9); + + // Get the current date + (uint256 currentYear, uint256 currentMonth, uint256 currentDay) = _timestampToDate(block.timestamp); + + // Get the expiration date + (uint256 expYear, uint256 expMonth, uint256 expDay) = _timestampToDate(secondsTimestamp); + + // Compare year, month, and day + if (expYear > currentYear) return true; + if (expYear == currentYear && expMonth > currentMonth) return true; + if (expYear == currentYear && expMonth == currentMonth && expDay > currentDay) return true; + + return false; + } + + function _timestampToDate(uint256 timestamp) internal pure returns (uint256 year, uint256 month, uint256 day) { + uint256 SECONDS_PER_DAY = 24 * 60 * 60; + uint256 DAYS_IN_YEAR = 365; + uint256 DAYS_IN_LEAP_YEAR = 366; + uint256 DAYS_IN_MONTH = 30; + + year = timestamp / (SECONDS_PER_DAY * DAYS_IN_YEAR); + month = (timestamp % (SECONDS_PER_DAY * DAYS_IN_YEAR)) / (SECONDS_PER_DAY * DAYS_IN_MONTH); + day = ((timestamp % (SECONDS_PER_DAY * DAYS_IN_YEAR)) % (SECONDS_PER_DAY * DAYS_IN_MONTH)) / SECONDS_PER_DAY; + } +*/ \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index 94e2ce18..6a895f38 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -39,6 +39,12 @@ const config: HardhatUserConfig = { compilers: [ { version: "0.8.27", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, }, ], }, diff --git a/helpers/DeployAnonAadharV1Validator.ts b/helpers/DeployAnonAadharV1Validator.ts new file mode 100644 index 00000000..bb99b7a5 --- /dev/null +++ b/helpers/DeployAnonAadharV1Validator.ts @@ -0,0 +1,116 @@ +import { ethers, upgrades } from "hardhat"; +import { Contract } from "ethers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; + +import { DeployHelper } from "./DeployHelper"; +import { OnchainIdentityDeployHelper } from "./OnchainIdentityDeployHelper"; +import { deployPoseidons } from "./PoseidonDeployHelper"; + +export class Groth16VerifierAnonAadhaarV1DeployHelper { + constructor( + private signers: SignerWithAddress[], + private readonly enableLogging: boolean = false, + ) {} + + static async initialize( + signers: SignerWithAddress[] | null = null, + enableLogging = true, + ): Promise { + let sgrs; + if (signers === null) { + sgrs = await ethers.getSigners(); + } else { + sgrs = signers; + } + + return new Groth16VerifierAnonAadhaarV1DeployHelper(sgrs, enableLogging); + } + + public async deployGroth16Wrapper(): Promise { + const owner = this.signers[0]; + + const verifierAnonAadhaarWrapper = await ethers.getContractFactory( + "Groth16VerifierAnonAadhaarV1Wrapper", + ); + const deployment = await verifierAnonAadhaarWrapper.deploy(); + await deployment.waitForDeployment(); + this.log( + `Identity contract deployed to address ${await deployment.getAddress()} from ${await owner.getAddress()}`, + ); + return deployment; + } + + public async deployAnonAadhaarV1Validator(stateContractAddress: string): Promise { + const groth16Wrapper = await this.deployGroth16Wrapper(); + + const owner = this.signers[0]; + + const verifierAddress = await groth16Wrapper.getAddress(); + + const validator = await ethers.getContractFactory("AnonAadhaarV1Validator"); + const deployment = await validator.deploy(); + await deployment.waitForDeployment(); + await deployment.initialize(verifierAddress, stateContractAddress, owner.getAddress()); + return deployment; + } + + public async deployIdentityLib(smtLibAddress: string): Promise { + const identityDeployHelper = await OnchainIdentityDeployHelper.initialize(); + const [poseidon3Elements, poseidon4Elements] = await deployPoseidons([3, 4]); + + const contracts = await identityDeployHelper.deployIdentityLib( + smtLibAddress, + await poseidon3Elements.getAddress(), + await poseidon4Elements.getAddress(), + ); + contracts.waitForDeployment(); + + return contracts; + } + + public async deployAnonAadhaarCredentialIssuing(): Promise { + const deployHelper = await DeployHelper.initialize(this.signers, true); + const state = await deployHelper.deployStateWithLibraries(); + await state.state.waitForDeployment(); + const stateContractAddress = await state.state.getAddress(); + + const anonAadhaarProofValidator = await this.deployAnonAadhaarV1Validator(stateContractAddress); + + const validatorAddress = await anonAadhaarProofValidator.getAddress(); + + console.log("id type", state.defaultIdType); + const verifierLib = await deployHelper.deployVerifierLib(); + const identityLib = await this.deployIdentityLib(await state.smtLib.getAddress()); + + const issuer = await ethers.getContractFactory("AnonAadhaarCredentialIssuing", { + libraries: { + VerifierLib: await verifierLib.getAddress(), + IdentityLib: await identityLib.getAddress(), + }, + }); + const deployment = await issuer.deploy(); + await deployment.waitForDeployment(); + const aadhaarIssuerTx = await deployment.initialize( + 12345678, + BigInt("15134874015316324267425466444584014077184337590635665158241104437045239495873"), + stateContractAddress, + state.defaultIdType, + ); + await aadhaarIssuerTx.wait(); + + const requestId = 940499666; // calculateRequestIdForCircuit(CircuitId.AuthV2); + + const setRequestTx = await deployment.setZKPRequest(requestId, { + metadata: "0x", + validator: validatorAddress, + data: "0x", + }); + await setRequestTx.wait(); + + return deployment; + } + + private log(...args): void { + this.enableLogging && console.log(args); + } +} diff --git a/helpers/constants.ts b/helpers/constants.ts index 354845dc..421ad9f4 100644 --- a/helpers/constants.ts +++ b/helpers/constants.ts @@ -338,6 +338,17 @@ export const contractsInfo = Object.freeze({ libraries: {}, }, }, + GROTH16_VERIFIER_ANON_AADHAAR_V1: { + name: "Groth16VerifierAnonAadhaarV1Wrapper", + unifiedAddress: "", + create2Calldata: "", + verificationOpts: { + contract: + "contracts/lib/groth16-verifiers/Groth16VerifierAnonAadhaarV1Wrapper.sol:Groth16VerifierAnonAadhaarV1Wrapper", + constructorArgsImplementation: [], + libraries: {}, + }, + }, STATE_LIB: { name: "StateLib", unifiedAddress: "", diff --git a/test/validators/sig/data/anon_aadhaar_proof.json b/test/validators/sig/data/anon_aadhaar_proof.json new file mode 100644 index 00000000..13b7fde5 --- /dev/null +++ b/test/validators/sig/data/anon_aadhaar_proof.json @@ -0,0 +1,41 @@ +{ + "pub_signals": [ + "15134874015316324267425466444584014077184337590635665158241104437045239495873", + "20883870714734602590504997352338571539423183306657935615322485719404078821399", + "18729648289086421571137268294888100218092815780174563740882479952354359161586", + "3375683699579602341351588179964014014035617303355018200724769264279740144593", + "4014752329325518869955150710481748021109011929967383408318766675788303116713", + "12345678", + "1001", + "1752685841", + "19885546056720838706860449020869651677281577675447204956487418402102594191373" + ], + "proof": { + "pi_a": [ + "6975148102362890318733286224648810647492172894642081364699578026825106424042", + "11744254957150939486190783489024046544506803323899444365339689384261671196583", + "1" + ], + "pi_b": [ + [ + "1159428455756792858340104700961471981067269931486244381539212669752794380024", + "7971647615217301430660362435022981135364187623023423870613900377711973572230" + ], + [ + "11397235344618348843723434438826626739007005733394010837036053896419415448084", + "3317112087765369456882388872999702851068407410424189029277568287263092387252" + ], + [ + "1", + "0" + ] + ], + "pi_c": [ + "11550543927212348039106493735277653324410491795293188141894207293714144034839", + "5939709037852542240984698506551237577030620157318154980836464594230693001981", + "1" + ], + "protocol": "groth16", + "curve": "bn128" + } +} \ No newline at end of file diff --git a/test/verifier/anonAadhaarVerifier.test.ts b/test/verifier/anonAadhaarVerifier.test.ts new file mode 100644 index 00000000..16faa523 --- /dev/null +++ b/test/verifier/anonAadhaarVerifier.test.ts @@ -0,0 +1,33 @@ +import { Groth16VerifierAnonAadhaarV1DeployHelper } from "../../helpers/DeployAnonAadharV1Validator"; +import { prepareInputs } from "../utils/state-utils"; +import proofJson from "../validators/sig/data/anon_aadhaar_proof.json"; +import { expect } from "chai"; +import { packZKProof } from "../utils/packData"; + +const emptyCrossChainProofs = new Uint8Array(); + +describe("Verify anon aadhaar proof onchain", async () => { + it("Verify proof and issuer a credential", async () => { + const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); + const inputsBytes = packZKProof(inputs, pi_a, pi_b, pi_c); + const factory = await Groth16VerifierAnonAadhaarV1DeployHelper.initialize(); + const deployment = await factory.deployAnonAadhaarCredentialIssuing(); + + const singleProof = [ + { + requestId: 940499666, + zkProof: inputsBytes, + data: "0x", + }, + ]; + + const tx = await deployment.submitZKPResponseV2(singleProof, emptyCrossChainProofs); + await tx.wait(); + const proof = await deployment.getClaimProof(inputs[3]); + expect(proof[1]).to.be.true; + + await expect( + deployment.submitZKPResponseV2(singleProof, emptyCrossChainProofs), + ).to.be.revertedWith("Nullifier already exists"); + }); +});