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");
+ });
+});