Skip to content
This repository has been archived by the owner on Oct 18, 2023. It is now read-only.

Commit

Permalink
Merge pull request #26 from Galactica-corp/basicNonRepeatableKYC
Browse files Browse the repository at this point in the history
Basic non repeatable kyc
  • Loading branch information
anhdungle93 authored Jul 25, 2023
2 parents d4e71e5 + afeee93 commit 6c32360
Show file tree
Hide file tree
Showing 9 changed files with 310 additions and 19 deletions.
86 changes: 86 additions & 0 deletions contracts/BasicKYCExampleDApp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "./VerificationSBT.sol";
import "./interfaces/IZkKYCVerifier.sol";

/**
* @title BasicKYCExampleDApp
* @author Galactica dev team
* @notice A simple DApp that requires a zkKYC proof to issue a Verification SBT.
* Registration can be repeated when the previous Verification SBT expired.
* The ZKP only check that the user has a valid zkKYC. It does not have other disclosures and does not include fraud investigation.
*/
contract BasicKYCExampleDApp {
VerificationSBT public SBT;
IZkKYCVerifier public verifierWrapper;

constructor(VerificationSBT _SBT, IZkKYCVerifier _verifierWrapper) {
SBT = _SBT;
verifierWrapper = _verifierWrapper;
}

/**
* @notice isVerified checks if an address provided a valid ZKP and got a verification SBT
* @param account the address to check
*/
function isVerified(address account) public view returns (bool) {
return SBT.isVerificationSBTValid(account, address(this));
}

function registerKYC(
uint[2] memory a,
uint[2][2] memory b,
uint[2] memory c,
uint[] memory input
) public {
require(
!SBT.isVerificationSBTValid(msg.sender, address(this)),
"The user already has a verification SBT. Please wait until it expires."
);

bytes32 humanID = bytes32(input[verifierWrapper.INDEX_HUMAN_ID()]);
uint dAppAddress = input[verifierWrapper.INDEX_DAPP_ID()];

// check that the public dAppAddress is correct
require(
dAppAddress == uint(uint160(address(this))),
"incorrect dAppAddress"
);

// check the zk proof
require(verifierWrapper.verifyProof(a, b, c, input), "invalid proof");

//afterwards we mint the verification SBT
uint256[2] memory userPubKey = [
input[verifierWrapper.INDEX_USER_PUBKEY_AX()],
input[verifierWrapper.INDEX_USER_PUBKEY_AY()]
];
uint amountInstitutions = verifierWrapper
.getAmountFraudInvestigationInstitutions();
bytes32[] memory encryptedData = new bytes32[](amountInstitutions * 2);
for (uint i = 0; i < amountInstitutions; i++) {
encryptedData[2 * i] = bytes32(
input[verifierWrapper.START_INDEX_ENCRYPTED_DATA() + 2 * i]
);
encryptedData[2 * i + 1] = bytes32(
input[verifierWrapper.START_INDEX_ENCRYPTED_DATA() + 2 * i + 1]
);
}
uint expirationTime = input[
verifierWrapper.INDEX_VERIFICATION_EXPIRATION()
];
uint256[2] memory providerPubKey = [
input[verifierWrapper.INDEX_PROVIDER_PUBKEY_AX()],
input[verifierWrapper.INDEX_PROVIDER_PUBKEY_AY()]
];
SBT.mintVerificationSBT(
msg.sender,
verifierWrapper,
expirationTime,
encryptedData,
userPubKey,
humanID,
providerPubKey
);
}
}
29 changes: 29 additions & 0 deletions scripts/deployBasicKYCExampleDApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* Copyright (C) 2023 Galactica Network. This file is part of zkKYC. zkKYC is 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. zkKYC 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 this program. If not, see <https://www.gnu.org/licenses/>. */
import hre from "hardhat";
import { deploySC } from '../lib/hardhatHelpers';

const log = console.log;


async function main() {
// parameters
const verificationSBT = '0xc1a96F7DD532fa4B774C41f9Eb853893314cB036';
const zkKYC = '0x4568bBf22031930e35F13E1A15BdF7a619a60539'; // you can reuse the zkKYC smart contract from the deployment of the RepeatableZKPTest

// wallets
const [deployer] = await hre.ethers.getSigners();
log(`Using account ${deployer.address} to deploy contracts`);
log(`Account balance: ${(await deployer.getBalance()).toString()}`);

// deploying everything
await deploySC('BasicKYCExampleDApp', true, {},
[verificationSBT, zkKYC]
);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
8 changes: 4 additions & 4 deletions scripts/deployDevnetGuardian.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ const log = console.log;
*/
async function main() {
// parameters
const centerRegistryAddr = '0x2547a64b4CDe45ad691BC393aBe20D405cD5139D';
const recordRegistryAddr = '0x8b7f9322F2CF92908eDB02a76DD8A2cAd6E566B5';
const centerRegistryAddr = '0x4De49e2047eE726B833fa815bf7392958245832d';
const recordRegistryAddr = '0x8eD8311ED65eBe2b11ED8cB7076E779c1030F9cF';

// wallets
const [ deployer ] = await hre.ethers.getSigners();
const [deployer] = await hre.ethers.getSigners();
log(`Using account ${deployer.address} to deploy contracts`);
log(`Account balance: ${(await deployer.getBalance()).toString()}`);

// deploying everything
const devnetGuardian = await deploySC('DevnetGuardian', true, {}, [recordRegistryAddr]);
log(`DevnetGuardian deployed to: ${devnetGuardian.address}`);
Expand Down
8 changes: 4 additions & 4 deletions scripts/deployExampleDApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ const log = console.log;

async function main() {
// parameters
const verificationSBT = '0x4E49d2383158568F5d4A30075e63614Dd7459060';
const ageProofZkKYC = '0x71d80ea7744302E5b1cFD61a7a26153FF221ca9E';
const verificationSBT = '0xc1a96F7DD532fa4B774C41f9Eb853893314cB036';
const ageProofZkKYC = '0x7790dDa9E7569bc3580E675D75Ad115E7B35c6ff';

// wallets
const [ deployer ] = await hre.ethers.getSigners();
const [deployer] = await hre.ethers.getSigners();
log(`Using account ${deployer.address} to deploy contracts`);
log(`Account balance: ${(await deployer.getBalance()).toString()}`);

// deploying everything
const mockDApp = await deploySC('MockDApp', true, {},
[verificationSBT, ageProofZkKYC]
Expand Down
4 changes: 2 additions & 2 deletions scripts/deployRepeatableZKPTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ const log = console.log;

async function main() {
// parameters
const verificationSBT = '0x4E49d2383158568F5d4A30075e63614Dd7459060';
const zkKYCRegistry = '0x855d8DeF49d550df733Afb848aC723AEEBa58adF';
const verificationSBT = '0xc1a96F7DD532fa4B774C41f9Eb853893314cB036';
const zkKYCRegistry = '0x8eD8311ED65eBe2b11ED8cB7076E779c1030F9cF';

// wallets
const [deployer] = await hre.ethers.getSigners();
Expand Down
10 changes: 5 additions & 5 deletions scripts/getMerkleProofFromChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { queryOnChainLeaves } from "../lib/queryMerkleTree";
* @description Script for creating a merkle tree for testing from a list of UTXOs, benchmark version
*/
async function main() {
const registryAddress = "0x8b7f9322F2CF92908eDB02a76DD8A2cAd6E566B5";
const registryAddress = "0x8eD8311ED65eBe2b11ED8cB7076E779c1030F9cF";
const leavesToProve = [
"19630604862894493237865119507631642105595355222686969752403793856928034143008",
"1722999490154515264044226908745492848723838509493895212716723397473228533371",
];

// Create a new poseidon instance for hashing
Expand All @@ -23,8 +23,8 @@ async function main() {
const merkleTree = new MerkleTree(merkleDepth, poseidon);
const leaves = await queryOnChainLeaves(ethers, registryAddress);
const batchSize = 10_000;
for (let i = 0; i < leaves.length; i += batchSize){
merkleTree.insertLeaves(leaves.slice(i, i+batchSize));
for (let i = 0; i < leaves.length; i += batchSize) {
merkleTree.insertLeaves(leaves.slice(i, i + batchSize));
}

console.log(`Merkle leaves: ${merkleTree.tree[0]}`)
Expand All @@ -43,7 +43,7 @@ async function main() {
console.log(`Merkle proof for ${leaf}:\n`, JSON.stringify(output, null, 2));
}
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
Expand Down
8 changes: 4 additions & 4 deletions scripts/issueZkKYC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import { fromDecToHex, fromHexToBytes32 } from "../lib/helpers";

async function main() {
// parameters
const centerRegistryAddr = '0x529ab188ecf1fA75FA5cA99c46ABb8a69e470075';
const recordRegistryAddr = '0x855d8DeF49d550df733Afb848aC723AEEBa58adF';
const centerRegistryAddr = '0x4De49e2047eE726B833fa815bf7392958245832d';
const recordRegistryAddr = '0x8eD8311ED65eBe2b11ED8cB7076E779c1030F9cF';
const zkKYCLeafHashes = [
'19630604862894493237865119507631642105595355222686969752403793856928034143008',
'13553445873695920927397403858740937949838667812849138092988169793799956616387',
'913338630289763938167212770624253461411251029088142596559861590717003723041',
];

// wallets
const [ deployer ] = await ethers.getSigners();
const [deployer] = await ethers.getSigners();
console.log(`Using account ${deployer.address} as KYC provider`);
console.log(`Account balance: ${(await deployer.getBalance()).toString()}`);
console.log();
Expand Down
175 changes: 175 additions & 0 deletions test/contracts/BasicKYCExampleDAppTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/* Copyright (C) 2023 Galactica Network. This file is part of zkKYC. zkKYC is 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. zkKYC 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 this program. If not, see <https://www.gnu.org/licenses/>. */
import { ethers } from 'hardhat';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
const snarkjs = require('snarkjs');
const hre = require('hardhat');

import chai from 'chai';
chai.config.includeStack = true;
const { expect } = chai;

import { MockKYCRegistry } from '../../typechain-types/contracts/mock/MockKYCRegistry';
import { BasicKYCExampleDApp } from '../../typechain-types/contracts/BasicKYCExampleDApp';
import { VerificationSBT } from '../../typechain-types/contracts/VerificationSBT';
import { ZkKYC } from '../../typechain-types/contracts/ZkKYC';
import { ZkKYCVerifier } from '../../typechain-types/contracts/ZkKYCVerifier';
import {
fromDecToHex,
processProof,
processPublicSignals,
fromHexToBytes32,
} from '../../lib/helpers';
import { generateSampleZkKYC, generateZkKYCProofInput } from '../../scripts/generateZKKYCInput';
import { ZKCertificate } from '../../lib/zkCertificate';


describe('BasicKYCExampleDApp', async () => {
// reset the testing chain so we can perform time related tests
/* await hre.network.provider.send('hardhat_reset'); */
let zkKycSC: ZkKYC;
let zkKYCVerifier: ZkKYCVerifier;
let mockKYCRegistry: MockKYCRegistry;
let verificationSBT: VerificationSBT;
let basicExampleDApp: BasicKYCExampleDApp;

let deployer: SignerWithAddress;
let user: SignerWithAddress;
let zkKYC: ZKCertificate;
let sampleInput: any, circuitWasmPath: string, circuitZkeyPath: string;

beforeEach(async () => {
// TODO: use fixture instead
// reset the testing chain so we can perform time related tests
await hre.network.provider.send('hardhat_reset');

[deployer, user] = await hre.ethers.getSigners();

// set up KYCRegistry, ZkKYCVerifier, ZkKYC
const mockKYCRegistryFactory = await ethers.getContractFactory(
'MockKYCRegistry',
deployer
);
mockKYCRegistry =
await mockKYCRegistryFactory.deploy() as MockKYCRegistry;

const zkKYCVerifierFactory = await ethers.getContractFactory(
'ZkKYCVerifier',
deployer
);
zkKYCVerifier =
await zkKYCVerifierFactory.deploy() as ZkKYCVerifier;

const zkKYCFactory = await ethers.getContractFactory(
'ZkKYC',
deployer
);
zkKycSC = (await zkKYCFactory.deploy(
deployer.address,
zkKYCVerifier.address,
mockKYCRegistry.address,
[]
)) as ZkKYC;
await zkKYCVerifier.deployed();

const verificationSBTFactory = await ethers.getContractFactory(
'VerificationSBT',
deployer
);
verificationSBT = await verificationSBTFactory.deploy() as VerificationSBT;


const repeatableZKPTestFactory = await ethers.getContractFactory(
'BasicKYCExampleDApp',
deployer
);
basicExampleDApp = await repeatableZKPTestFactory.deploy(
verificationSBT.address,
zkKycSC.address,
) as BasicKYCExampleDApp;

// inputs to create proof
zkKYC = await generateSampleZkKYC();
sampleInput = await generateZkKYCProofInput(zkKYC, 0, basicExampleDApp.address);
sampleInput.dAppAddress = basicExampleDApp.address;

// advance time a bit to set it later in the test
sampleInput.currentTime += 100;

// get signer object authorized to use the zkKYC record
user = await hre.ethers.getImpersonatedSigner(sampleInput.userAddress);

circuitWasmPath = './circuits/build/zkKYC.wasm';
circuitZkeyPath = './circuits/build/zkKYC.zkey';
});

it('should issue VerificationSBT on correct proof and refuse to re-register before expiration', async () => {
let { proof, publicSignals } = await snarkjs.groth16.fullProve(
sampleInput,
circuitWasmPath,
circuitZkeyPath
);

const publicRoot = publicSignals[await zkKycSC.INDEX_ROOT()];
const publicTime = parseInt(publicSignals[await zkKycSC.INDEX_CURRENT_TIME()], 10);
// set the merkle root to the correct one
await mockKYCRegistry.setMerkleRoot(
fromHexToBytes32(fromDecToHex(publicRoot))
);

// set time to the public time
await hre.network.provider.send('evm_setNextBlockTimestamp', [publicTime]);
await hre.network.provider.send('evm_mine');

let [a, b, c] = processProof(proof);

let publicInputs = processPublicSignals(publicSignals);
await basicExampleDApp.connect(user).registerKYC(a, b, c, publicInputs);

expect(await verificationSBT.isVerificationSBTValid(user.address, basicExampleDApp.address)).to.be.true;

expect(basicExampleDApp.connect(user).registerKYC(a, b, c, publicInputs)).to.be.revertedWith("user already has a verification SBT");

// wait until verification SBT expires to renew it
const sbt = await verificationSBT.getVerificationSBTInfo(user.address, basicExampleDApp.address);
let laterProofInput = { ...sampleInput };
laterProofInput.currentTime = sbt.expirationTime.toNumber() + 1;
await hre.network.provider.send('evm_setNextBlockTimestamp', [laterProofInput.currentTime]);
await hre.network.provider.send('evm_mine');

expect(await verificationSBT.isVerificationSBTValid(user.address, basicExampleDApp.address)).to.be.false;

let laterProof = await snarkjs.groth16.fullProve(
laterProofInput,
circuitWasmPath,
circuitZkeyPath
);
[a, b, c] = processProof(laterProof.proof);
publicInputs = processPublicSignals(laterProof.publicSignals);
await basicExampleDApp.connect(user).registerKYC(a, b, c, laterProof.publicSignals);

expect(await verificationSBT.isVerificationSBTValid(user.address, basicExampleDApp.address)).to.be.true;
});

it('should catch incorrect proof', async () => {
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
sampleInput,
circuitWasmPath,
circuitZkeyPath
);

const publicRoot = publicSignals[await zkKycSC.INDEX_ROOT()];
// set the merkle root to the correct one

await mockKYCRegistry.setMerkleRoot(
fromHexToBytes32(fromDecToHex(publicRoot))
);
let [a, b, c] = processProof(proof);

let publicInputs = processPublicSignals(publicSignals);

// switch c, a to get an incorrect proof
// it doesn't fail on time because the time change remains from the previous test
await expect(basicExampleDApp.connect(user).registerKYC(c, b, a, publicInputs))
.to.be.reverted;
});
});
Loading

0 comments on commit 6c32360

Please sign in to comment.