diff --git a/solidity/contracts/factory.sol b/solidity/contracts/factory.sol index 533c93f..53fded3 100644 --- a/solidity/contracts/factory.sol +++ b/solidity/contracts/factory.sol @@ -53,7 +53,6 @@ contract ZetoTokenFactory is Ownable { ); // the depositVerifier and withdrawVerifier are optional // for the non-fungible token implementations - // TODO batchVerifier to be implemented for non-fungible token implementations[name] = implementation; } diff --git a/solidity/ignition/modules/zeto_anon.ts b/solidity/ignition/modules/zeto_anon.ts index 94e7702..efbb0ae 100644 --- a/solidity/ignition/modules/zeto_anon.ts +++ b/solidity/ignition/modules/zeto_anon.ts @@ -23,13 +23,13 @@ const VerifierModule = buildModule('Groth16Verifier_Anon', (m) => { }); const BatchVerifierModule = buildModule('Groth16Verifier_AnonBatch', (m) => { - const batchVerifier = m.contract('Groth16Verifier_AnonBatch', []); - return { batchVerifier }; + const verifier = m.contract('Groth16Verifier_AnonBatch', []); + return { verifier }; }); export default buildModule('Zeto_Anon', (m) => { const { verifier } = m.useModule(VerifierModule); - const { batchVerifier } = m.useModule(BatchVerifierModule); + const { verifier: batchVerifier } = m.useModule(BatchVerifierModule); const { verifier: depositVerifier } = m.useModule(DepositVerifierModule); const { verifier: withdrawVerifier } = m.useModule(WithdrawVerifierModule); diff --git a/solidity/ignition/modules/zeto_anon_enc.ts b/solidity/ignition/modules/zeto_anon_enc.ts index 350ad0a..6a0ea63 100644 --- a/solidity/ignition/modules/zeto_anon_enc.ts +++ b/solidity/ignition/modules/zeto_anon_enc.ts @@ -23,13 +23,13 @@ const VerifierModule = buildModule('Groth16Verifier_AnonEnc', (m) => { }); const BatchVerifierModule = buildModule('Groth16Verifier_AnonEncBatch', (m) => { - const batchVerifier = m.contract('Groth16Verifier_AnonEncBatch', []); - return { batchVerifier }; + const verifier = m.contract('Groth16Verifier_AnonEncBatch', []); + return { verifier }; }); export default buildModule('Zeto_AnonEnc', (m) => { const { verifier } = m.useModule(VerifierModule); - const { batchVerifier } = m.useModule(BatchVerifierModule); + const { verifier: batchVerifier } = m.useModule(BatchVerifierModule); const { verifier: depositVerifier } = m.useModule(DepositVerifierModule); const { verifier: withdrawVerifier } = m.useModule(WithdrawVerifierModule); return { depositVerifier, withdrawVerifier, verifier, batchVerifier }; diff --git a/solidity/ignition/modules/zeto_anon_enc_nullifier.ts b/solidity/ignition/modules/zeto_anon_enc_nullifier.ts index 8bcca3e..1d2ae4c 100644 --- a/solidity/ignition/modules/zeto_anon_enc_nullifier.ts +++ b/solidity/ignition/modules/zeto_anon_enc_nullifier.ts @@ -29,18 +29,15 @@ const VerifierModule = buildModule('Groth16Verifier_AnonEncNullifier', (m) => { const BatchVerifierModule = buildModule( 'Groth16Verifier_AnonEncNullifierBatch', (m) => { - const batchVerifier = m.contract( - 'Groth16Verifier_AnonEncNullifierBatch', - [] - ); - return { batchVerifier }; + const verifier = m.contract('Groth16Verifier_AnonEncNullifierBatch', []); + return { verifier }; } ); export default buildModule('Zeto_AnonEncNullifier', (m) => { const { smtLib, poseidon3 } = m.useModule(SmtLibModule); const { verifier } = m.useModule(VerifierModule); - const { batchVerifier } = m.useModule(BatchVerifierModule); + const { verifier: batchVerifier } = m.useModule(BatchVerifierModule); const { verifier: depositVerifier } = m.useModule(DepositVerifierModule); const { verifier: withdrawVerifier } = m.useModule( WithdrawNullifierVerifierModule diff --git a/solidity/ignition/modules/zeto_anon_enc_nullifier_kyc.ts b/solidity/ignition/modules/zeto_anon_enc_nullifier_kyc.ts index f244fe9..c6e04bd 100644 --- a/solidity/ignition/modules/zeto_anon_enc_nullifier_kyc.ts +++ b/solidity/ignition/modules/zeto_anon_enc_nullifier_kyc.ts @@ -32,18 +32,15 @@ const VerifierModule = buildModule( const BatchVerifierModule = buildModule( 'Groth16Verifier_AnonEncNullifierKycBatch', (m) => { - const batchVerifier = m.contract( - 'Groth16Verifier_AnonEncNullifierKycBatch', - [] - ); - return { batchVerifier }; + const verifier = m.contract('Groth16Verifier_AnonEncNullifierKycBatch', []); + return { verifier }; } ); export default buildModule('Zeto_AnonEncNullifierKyc', (m) => { const { smtLib, poseidon2, poseidon3 } = m.useModule(SmtLibModule); const { verifier } = m.useModule(VerifierModule); - const { batchVerifier } = m.useModule(BatchVerifierModule); + const { verifier: batchVerifier } = m.useModule(BatchVerifierModule); const { verifier: depositVerifier } = m.useModule(DepositVerifierModule); const { verifier: withdrawVerifier } = m.useModule( WithdrawNullifierVerifierModule diff --git a/solidity/ignition/modules/zeto_anon_enc_nullifier_non_repudiation.ts b/solidity/ignition/modules/zeto_anon_enc_nullifier_non_repudiation.ts index bcac005..7655983 100644 --- a/solidity/ignition/modules/zeto_anon_enc_nullifier_non_repudiation.ts +++ b/solidity/ignition/modules/zeto_anon_enc_nullifier_non_repudiation.ts @@ -35,18 +35,18 @@ const VerifierModule = buildModule( const BatchVerifierModule = buildModule( 'Groth16Verifier_AnonEncNullifierNonRepudiationBatch', (m) => { - const batchVerifier = m.contract( + const verifier = m.contract( 'Groth16Verifier_AnonEncNullifierNonRepudiationBatch', [] ); - return { batchVerifier }; + return { verifier }; } ); export default buildModule('Zeto_AnonEncNullifierNonRepudiation', (m) => { const { smtLib, poseidon3 } = m.useModule(SmtLibModule); const { verifier } = m.useModule(VerifierModule); - const { batchVerifier } = m.useModule(BatchVerifierModule); + const { verifier: batchVerifier } = m.useModule(BatchVerifierModule); const { verifier: depositVerifier } = m.useModule(DepositVerifierModule); const { verifier: withdrawVerifier } = m.useModule( WithdrawNullifierVerifierModule diff --git a/solidity/ignition/modules/zeto_anon_nullifier.ts b/solidity/ignition/modules/zeto_anon_nullifier.ts index 8c08f1e..9d68d86 100644 --- a/solidity/ignition/modules/zeto_anon_nullifier.ts +++ b/solidity/ignition/modules/zeto_anon_nullifier.ts @@ -29,15 +29,15 @@ const VerifierModule = buildModule('Groth16Verifier_AnonNullifier', (m) => { const BatchVerifierModule = buildModule( 'Groth16Verifier_AnonNullifierBatch', (m) => { - const batchVerifier = m.contract('Groth16Verifier_AnonNullifierBatch', []); - return { batchVerifier }; + const verifier = m.contract('Groth16Verifier_AnonNullifierBatch', []); + return { verifier }; } ); export default buildModule('Zeto_AnonNullifier', (m) => { const { smtLib, poseidon3 } = m.useModule(SmtLibModule); const { verifier } = m.useModule(VerifierModule); - const { batchVerifier } = m.useModule(BatchVerifierModule); + const { verifier: batchVerifier } = m.useModule(BatchVerifierModule); const { verifier: depositVerifier } = m.useModule(DepositVerifierModule); const { verifier: withdrawVerifier } = m.useModule( WithdrawNullifierVerifierModule diff --git a/solidity/ignition/modules/zeto_anon_nullifier_kyc.ts b/solidity/ignition/modules/zeto_anon_nullifier_kyc.ts index 1480714..47a7032 100644 --- a/solidity/ignition/modules/zeto_anon_nullifier_kyc.ts +++ b/solidity/ignition/modules/zeto_anon_nullifier_kyc.ts @@ -29,18 +29,15 @@ const VerifierModule = buildModule('Groth16Verifier_AnonNullifierKyc', (m) => { const BatchVerifierModule = buildModule( 'Groth16Verifier_AnonNullifierKycBatch', (m) => { - const batchVerifier = m.contract( - 'Groth16Verifier_AnonNullifierKycBatch', - [] - ); - return { batchVerifier }; + const verifier = m.contract('Groth16Verifier_AnonNullifierKycBatch', []); + return { verifier }; } ); export default buildModule('Zeto_AnonNullifierKyc', (m) => { const { smtLib, poseidon2, poseidon3 } = m.useModule(SmtLibModule); const { verifier } = m.useModule(VerifierModule); - const { batchVerifier } = m.useModule(BatchVerifierModule); + const { verifier: batchVerifier } = m.useModule(BatchVerifierModule); const { verifier: depositVerifier } = m.useModule(DepositVerifierModule); const { verifier: withdrawVerifier } = m.useModule( WithdrawNullifierVerifierModule diff --git a/solidity/scripts/deploy_cloneable.ts b/solidity/scripts/deploy_cloneable.ts index 54af872..6c6a4ef 100644 --- a/solidity/scripts/deploy_cloneable.ts +++ b/solidity/scripts/deploy_cloneable.ts @@ -1,21 +1,23 @@ -import { ethers, ignition } from "hardhat"; +import { ethers, ignition } from 'hardhat'; import erc20Module from '../ignition/modules/erc20'; -import { getLinkedContractFactory, deploy } from "./lib/common"; +import { getLinkedContractFactory, deploy } from './lib/common'; export async function deployFungible(tokenName: string) { const { erc20 } = await ignition.deploy(erc20Module); const verifiersDeployer = require(`./tokens/${tokenName}`); - const { deployer, args, libraries } = await verifiersDeployer.deployDependencies(); + const { deployer, args, libraries } = + await verifiersDeployer.deployDependencies(); let zetoFactory; if (libraries) { zetoFactory = await getLinkedContractFactory(tokenName, libraries); } else { - zetoFactory = await ethers.getContractFactory(tokenName) + zetoFactory = await ethers.getContractFactory(tokenName); } const zetoImpl: any = await zetoFactory.deploy(); await zetoImpl.waitForDeployment(); + // console.log(args); await zetoImpl.connect(deployer).initialize(...args); const tx3 = await zetoImpl.connect(deployer).setERC20(erc20.target); @@ -36,7 +38,7 @@ export async function deployNonFungible(tokenName: string) { if (libraries) { zetoFactory = await getLinkedContractFactory(tokenName, libraries); } else { - zetoFactory = await ethers.getContractFactory(tokenName) + zetoFactory = await ethers.getContractFactory(tokenName); } const zetoImpl: any = await zetoFactory.deploy(); await zetoImpl.waitForDeployment(); @@ -54,7 +56,7 @@ deploy(deployFungible, deployNonFungible) } process.exit(0); }) - .catch(error => { + .catch((error) => { console.error(error); process.exit(1); - }); \ No newline at end of file + }); diff --git a/solidity/test/gas_cost/zeto_anon_enc_nullifier_kyc_cost_analysis.ts b/solidity/test/gas_cost/zeto_anon_enc_nullifier_kyc_cost_analysis.ts index 6db235c..8a13a60 100644 --- a/solidity/test/gas_cost/zeto_anon_enc_nullifier_kyc_cost_analysis.ts +++ b/solidity/test/gas_cost/zeto_anon_enc_nullifier_kyc_cost_analysis.ts @@ -44,9 +44,10 @@ import { } from '../utils'; import { deployZeto } from '../lib/deploy'; -const TOTAL_AMOUNT = parseInt(process.env.TOTAL_ROUNDS || '1000'); +const TOTAL_AMOUNT = parseInt(process.env.TOTAL_ROUNDS || '40'); const TX_CONCURRENCY = parseInt(process.env.TX_CONCURRENCY || '30'); -const UTXO_PER_TX = parseInt(process.env.UTXO_PER_TX || '2'); + +const UTXO_PER_TX = 10; describe.skip('(Gas cost analysis) Zeto based fungible token with anonymity using nullifiers and encryption with KYC', function () { let deployer: Signer; @@ -55,9 +56,8 @@ describe.skip('(Gas cost analysis) Zeto based fungible token with anonymity usin let Charlie: User; let erc20: any; let zeto: any; - const atMostBatchedAmount = Math.floor(TOTAL_AMOUNT / UTXO_PER_TX); - const atLeastBatchedAmount = - atMostBatchedAmount + (TOTAL_AMOUNT % UTXO_PER_TX); + const atMostHalfAmount = Math.floor(TOTAL_AMOUNT / 2); + const atLeastHalfAmount = atMostHalfAmount + (TOTAL_AMOUNT % 2); let unspentAliceUTXOs: UTXO[] = []; let unspentBobUTXOs: UTXO[] = []; let mintGasCostHistory: number[] = []; @@ -130,33 +130,33 @@ describe.skip('(Gas cost analysis) Zeto based fungible token with anonymity usin const startingBalance = await erc20.balanceOf(Alice.ethAddress); const tx = await erc20 .connect(deployer) - .mint(Alice.ethAddress, atMostBatchedAmount); // mint to alice the amount of token that can be deposited + .mint(Alice.ethAddress, atMostHalfAmount); // mint to alice the amount of token that can be deposited await tx.wait(); const endingBalance = await erc20.balanceOf(Alice.ethAddress); - expect(endingBalance - startingBalance).to.be.equal(atMostBatchedAmount); + expect(endingBalance - startingBalance).to.be.equal(atMostHalfAmount); console.log( - `ERC20 successfully minted ${atMostBatchedAmount} to Alice for deposit` + `ERC20 successfully minted ${atMostHalfAmount} to Alice for deposit` ); const tx1 = await erc20 .connect(Alice.signer) - .approve(zeto.target, atMostBatchedAmount); + .approve(zeto.target, atMostHalfAmount); await tx1.wait(); const startingBalanceDep = await erc20.balanceOf(zeto.target); const txDep = await erc20 .connect(deployer) - .mint(zeto.target, atLeastBatchedAmount); // mint to zeto contract the amount of token that can be minted + .mint(zeto.target, atLeastHalfAmount); // mint to zeto contract the amount of token that can be minted await txDep.wait(); const endingBalanceDep = await erc20.balanceOf(zeto.target); expect(endingBalanceDep - startingBalanceDep).to.be.equal( - atLeastBatchedAmount + atLeastHalfAmount ); }); - it(`Alice deposit ${atMostBatchedAmount} token`, async function () { + it(`Alice deposit ${atMostHalfAmount} token`, async function () { let promises = []; - for (let i = 0; i < atMostBatchedAmount; i++) { + for (let i = 0; i < atMostHalfAmount; i++) { promises.push( (async () => { const utxoSingle = newUTXO(1, Alice); @@ -193,42 +193,34 @@ describe.skip('(Gas cost analysis) Zeto based fungible token with anonymity usin ); }).timeout(6000000000000); - it(`Zeto mint ${ - atMostBatchedAmount + (TOTAL_AMOUNT % 2) - } token to Alice in ${ - Math.floor(atLeastBatchedAmount / 2) + (atLeastBatchedAmount % 2) + it(`Zeto mint ${atMostHalfAmount + (TOTAL_AMOUNT % 2)} token to Alice in ${ + Math.floor(atLeastHalfAmount / UTXO_PER_TX) + + (atLeastHalfAmount % 2 !== 0 ? 1 : 0) } txs`, async function () { const mintRounds = - Math.floor(atLeastBatchedAmount / 2) + (atLeastBatchedAmount % 2); + Math.floor(atLeastHalfAmount / UTXO_PER_TX) + + (atLeastHalfAmount % 2 !== 0 ? 1 : 0); let promises = []; for (let i = 0; i < mintRounds; i++) { promises.push( (async () => { - const utxo1 = newUTXO(1, Alice); - let utxo2 = newUTXO(1, Alice); - - if (i === mintRounds - 1 && atLeastBatchedAmount % 2 === 1) { - utxo2 = newUTXO(0, Alice); // odd number + const mintUTXOs = []; + for (let j = 0; j < UTXO_PER_TX; j++) { + if ( + i !== mintRounds - 1 || + atLeastHalfAmount % 2 === 0 || + j < atLeastHalfAmount % 2 + ) { + const _new_utxo = newUTXO(1, Alice); + mintUTXOs.push(_new_utxo); + + await smtAlice.add(_new_utxo.hash, _new_utxo.hash); + await smtBob.add(_new_utxo.hash, _new_utxo.hash); + unspentAliceUTXOs.push(_new_utxo); + } } - const result1 = await doMint( - zeto, - deployer, - [utxo1, utxo2], - mintGasCostHistory - ); - const mintEvents = parseUTXOEvents(zeto, result1); - const [_utxo1, _utxo2] = mintEvents[0].outputs; - - // Add UTXOs to the sets - await smtAlice.add(_utxo1, _utxo1); - await smtBob.add(_utxo1, _utxo1); - await smtAlice.add(_utxo2, _utxo2); - await smtBob.add(_utxo2, _utxo2); - - // Save unspent UTXOs - unspentAliceUTXOs.push(utxo1); - unspentAliceUTXOs.push(utxo2); + await doMint(zeto, deployer, mintUTXOs, mintGasCostHistory); })() ); @@ -249,8 +241,19 @@ describe.skip('(Gas cost analysis) Zeto based fungible token with anonymity usin ); }).timeout(6000000000000); - it(`Alice transfer ${TOTAL_AMOUNT} tokens to Bob in ${atLeastBatchedAmount} txs`, async function () { - const totalTxs = Math.floor(unspentAliceUTXOs.length / 2); + it(`Alice transfer ${TOTAL_AMOUNT} tokens to Bob in ${ + Math.floor(unspentAliceUTXOs.length / UTXO_PER_TX) + + (unspentAliceUTXOs.length % UTXO_PER_TX) !== + 0 + ? 1 + : 0 + } txs`, async function () { + const totalTxs = + Math.floor(unspentAliceUTXOs.length / UTXO_PER_TX) + + (unspentAliceUTXOs.length % UTXO_PER_TX) !== + 0 + ? 1 + : 0; const utxosRoot = await smtAlice.root(); // get the root before all transfer and use it for all the proofs let promises = []; // Alice generates inclusion proofs for the identities in the transaction @@ -265,57 +268,61 @@ describe.skip('(Gas cost analysis) Zeto based fungible token with anonymity usin ); const identityMerkleProofs = [ proof3.siblings.map((s) => s.bigInt()), // identity proof for the sender (Alice) - proof4.siblings.map((s) => s.bigInt()), // identity proof for the 1st owner of the output UTXO (Bob) - proof4.siblings.map((s) => s.bigInt()), // identity proof for the 2nd owner of the output UTXO (Bob) ]; + const batchSize = UTXO_PER_TX > 2 ? 10 : 2; + + for (let i = 0; i < batchSize; i++) { + identityMerkleProofs.push(proof4.siblings.map((s) => s.bigInt())); // identity proof for the output utxos (Bob) + } + for (let i = 0; i < totalTxs; i++) { promises.push( (async () => { - const utxo1 = unspentAliceUTXOs[i * 2]; - const utxo2 = unspentAliceUTXOs[i * 2 + 1]; - - const newUtxo1 = newUTXO(1, Bob); - let newUtxo2 = newUTXO(1, Bob); - if (i === totalTxs - 1 && TOTAL_AMOUNT % 2 === 1) { - // last round - newUtxo2 = newUTXO(0, Bob); // odd number + const _inUtxos = []; + const _outUtxos = []; + const _mtps = []; + const _nullifiers = []; + for (let j = 0; j < UTXO_PER_TX; j++) { + if ( + i !== totalTxs - 1 || + unspentAliceUTXOs.length % UTXO_PER_TX === 0 || + j < unspentAliceUTXOs.length % UTXO_PER_TX + ) { + const _iUtxo = unspentAliceUTXOs[i * UTXO_PER_TX + j]; + _inUtxos.push(_iUtxo); + _nullifiers.push(newNullifier(_iUtxo, Alice)); + // Alice generates inclusion proofs for the UTXOs to be spent + const inProof = await smtAlice.generateCircomVerifierProof( + _iUtxo.hash, + utxosRoot + ); + _mtps.push(inProof.siblings.map((s) => s.bigInt())); + const _oUtox = newUTXO(1, Bob); + _outUtxos.push(_oUtox); + await smtAlice.add(_oUtox.hash, _oUtox.hash); + await smtBob.add(_oUtox.hash, _oUtox.hash); + unspentBobUTXOs.push(_oUtox); + } + } + const owners = []; + for (let i = 0; i < batchSize; i++) { + owners.push(Bob); } - const nullifier1 = newNullifier(utxo1, Alice); - const nullifier2 = newNullifier(utxo2, Alice); - // Alice generates inclusion proofs for the UTXOs to be spent - const proof1 = await smtAlice.generateCircomVerifierProof( - utxo1.hash, - utxosRoot - ); - const proof2 = await smtAlice.generateCircomVerifierProof( - utxo2.hash, - utxosRoot - ); - const utxoMerkleProofs = [ - proof1.siblings.map((s) => s.bigInt()), - proof2.siblings.map((s) => s.bigInt()), - ]; // Alice transfers her UTXOs to Bob await doTransfer( Alice, - [utxo1, utxo2], - [nullifier1, nullifier2], - [newUtxo1, newUtxo2], + _inUtxos, + _nullifiers, + _outUtxos, utxosRoot.bigInt(), - utxoMerkleProofs, + _mtps, identitiesRoot.bigInt(), identityMerkleProofs, - [Bob, Bob], + owners, transferGasCostHistory ); - await smtAlice.add(newUtxo1.hash, newUtxo1.hash); - await smtBob.add(newUtxo1.hash, newUtxo1.hash); - await smtAlice.add(newUtxo2.hash, newUtxo2.hash); - await smtBob.add(newUtxo2.hash, newUtxo2.hash); - unspentBobUTXOs.push(newUtxo1); - unspentBobUTXOs.push(newUtxo2); })() ); diff --git a/solidity/test/lib/deploy.ts b/solidity/test/lib/deploy.ts index f85a955..e893726 100644 --- a/solidity/test/lib/deploy.ts +++ b/solidity/test/lib/deploy.ts @@ -52,9 +52,9 @@ export async function deployZeto(tokenName: string) { const [ deployerAddr, verifier, - batchVerifier, depositVerifier, withdrawVerifier, + batchVerifier, ] = args; // we want to test the effectiveness of the factory contract @@ -70,8 +70,10 @@ export async function deployZeto(tokenName: string) { withdrawVerifier: withdrawVerifier || '0x0000000000000000000000000000000000000000', verifier, - batchVerifier, + batchVerifier: + batchVerifier || '0x0000000000000000000000000000000000000000', }; + // console.log(implInfo); const tx1 = await factory .connect(deployer) .registerImplementation(tokenName, implInfo as any); diff --git a/zkp/circuits/gen.js b/zkp/circuits/gen.js index 9ebd25b..e96887a 100644 --- a/zkp/circuits/gen.js +++ b/zkp/circuits/gen.js @@ -45,7 +45,7 @@ const ptauDownload = process.env.PTAU_DOWNLOAD_PATH || argv.ptauDownloadPath; const specificCircuits = argv.c; const verbose = argv.v; const compileOnly = argv.compileOnly; -const parallelLimit = parseInt(process.env.GEN_CONCURRENCY, 10) || 10; // Default to compile 10 circuits in parallel +const parallelLimit = parseInt(process.env.GEN_CONCURRENCY, 10) || 30; // Default to compile 30 circuits in parallel // check env vars if (!circuitsRoot) {