From f2c17c2d73848f0120ccb0cd7f7e3459517a856e Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 13 Aug 2024 12:04:12 -0400 Subject: [PATCH 01/10] Circuit compile and proving key gen enhancements Signed-off-by: Jim Zhang --- .github/workflows/e2e.yaml | 7 ++-- zkp/circuits/gen-config.json | 4 -- zkp/circuits/gen.js | 78 ++++++++++++++++-------------------- zkp/circuits/package.json | 3 +- zkp/js/index.js | 8 +++- 5 files changed, 46 insertions(+), 54 deletions(-) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index db4e451..e1b9164 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -52,6 +52,7 @@ jobs: - name: Build circuits env: + CIRCUITS_ROOT: ${{ runner.temp }}/zeto-artifacts PROVING_KEYS_ROOT: ${{ runner.temp }}/zeto-artifacts PTAU_DOWNLOAD_PATH: ${{ runner.temp }}/zeto-artifacts working-directory: zkp/circuits @@ -62,7 +63,7 @@ jobs: - name: Run golang e2e tests env: PROVING_KEYS_ROOT: ${{ runner.temp }}/zeto-artifacts - CIRCUITS_ROOT: ${{ github.workspace }}/zkp/js/lib + CIRCUITS_ROOT: ${{ runner.temp }}/zeto-artifacts working-directory: go-sdk run: | make e2e @@ -70,7 +71,7 @@ jobs: - name: Run js e2e tests env: PROVING_KEYS_ROOT: ${{ runner.temp }}/zeto-artifacts - CIRCUITS_ROOT: ${{ github.workspace }}/zkp/js/lib + CIRCUITS_ROOT: ${{ runner.temp }}/zeto-artifacts working-directory: zkp/js run: | npm install @@ -79,7 +80,7 @@ jobs: - name: Run Zeto Tokens hardhat tests env: PROVING_KEYS_ROOT: ${{ runner.temp }}/zeto-artifacts - CIRCUITS_ROOT: ${{ github.workspace }}/zkp/js/lib + CIRCUITS_ROOT: ${{ runner.temp }}/zeto-artifacts working-directory: solidity run: | npm install diff --git a/zkp/circuits/gen-config.json b/zkp/circuits/gen-config.json index 7c43f16..12817a7 100644 --- a/zkp/circuits/gen-config.json +++ b/zkp/circuits/gen-config.json @@ -39,10 +39,6 @@ "ptau": "powersOfTau28_hez_final_16", "skipSolidityGenaration": false }, - "check_smt_proof": { - "ptau": "powersOfTau28_hez_final_16", - "skipSolidityGenaration": false - }, "check_nullifiers": { "ptau": "powersOfTau28_hez_final_11", "skipSolidityGenaration": true diff --git a/zkp/circuits/gen.js b/zkp/circuits/gen.js index 9c7004d..51a2bc3 100644 --- a/zkp/circuits/gen.js +++ b/zkp/circuits/gen.js @@ -3,20 +3,29 @@ const path = require('path'); const { exec } = require('child_process'); const { promisify } = require('util'); const axios = require('axios'); +const yargs = require('yargs/yargs'); +const { hideBin } = require('yargs/helpers'); +const argv = yargs(hideBin(process.argv)).argv; +const circuitsRoot = process.env.CIRCUITS_ROOT; const provingKeysRoot = process.env.PROVING_KEYS_ROOT; const ptauDownload = process.env.PTAU_DOWNLOAD_PATH; -const specificCircuit = process.argv[2]; +const specificCircuits = argv.c; +const compileOnly = argv.compileOnly; const parallelLimit = parseInt(process.env.GEN_CONCURRENCY, 10) || 10; // Default to compile 10 circuits in parallel // check env vars +if (!circuitsRoot) { + console.error('Error: CIRCUITS_ROOT is not set.'); + process.exit(1); +} -if (!provingKeysRoot) { +if (!compileOnly && !provingKeysRoot) { console.error('Error: PROVING_KEYS_ROOT is not set.'); process.exit(1); } -if (!ptauDownload) { +if (!compileOnly && !ptauDownload) { console.error('Error: PTAU_DOWNLOAD_PATH is not set.'); process.exit(1); } @@ -54,15 +63,12 @@ const processCircuit = async (circuit, ptau, skipSolidityGenaration) => { return; } - if (!fs.existsSync(ptauFile)) { + if (!compileOnly && !fs.existsSync(ptauFile)) { log(circuit, `PTAU file does not exist, downloading: ${ptauFile}`); try { - const response = await axios.get( - `https://storage.googleapis.com/zkevm/ptau/${ptau}.ptau`, - { - responseType: 'stream', - } - ); + const response = await axios.get(`https://storage.googleapis.com/zkevm/ptau/${ptau}.ptau`, { + responseType: 'stream', + }); response.data.pipe(fs.createWriteStream(ptauFile)); await new Promise((resolve, reject) => { response.data.on('end', resolve); @@ -75,24 +81,18 @@ const processCircuit = async (circuit, ptau, skipSolidityGenaration) => { } log(circuit, `Compiling circuit`); - await execAsync(`circom ${circomInput} --output ../js/lib --sym --wasm`); + await execAsync(`circom ${circomInput} --output ${circuitsRoot} --sym --wasm`); + if (compileOnly) { + return; + } + await execAsync(`circom ${circomInput} --output ${provingKeysRoot} --r1cs`); log(circuit, `Generating test proving key with ${ptau}`); - await execAsync( - `snarkjs groth16 setup ${path.join( - provingKeysRoot, - `${circuit}.r1cs` - )} ${ptauFile} ${zkeyOutput}` - ); + await execAsync(`snarkjs groth16 setup ${path.join(provingKeysRoot, `${circuit}.r1cs`)} ${ptauFile} ${zkeyOutput}`); log(circuit, `Generating verification key`); - await execAsync( - `snarkjs zkey export verificationkey ${zkeyOutput} ${path.join( - provingKeysRoot, - `${circuit}-vkey.json` - )}` - ); + await execAsync(`snarkjs zkey export verificationkey ${zkeyOutput} ${path.join(provingKeysRoot, `${circuit}-vkey.json`)}`); if (skipSolidityGenaration) { log(circuit, `Skipping solidity verifier generation`); @@ -100,37 +100,27 @@ const processCircuit = async (circuit, ptau, skipSolidityGenaration) => { } log(circuit, `Generating solidity verifier`); - const solidityFile = path.join( - '..', - '..', - 'solidity', - 'contracts', - 'lib', - `verifier_${circuit}.sol` - ); - await execAsync( - `snarkjs zkey export solidityverifier ${zkeyOutput} ${solidityFile}` - ); + const solidityFile = path.join('..', '..', 'solidity', 'contracts', 'lib', `verifier_${circuit}.sol`); + await execAsync(`snarkjs zkey export solidityverifier ${zkeyOutput} ${solidityFile}`); log(circuit, `Modifying the contract name in the Solidity file`); const camelCaseCircuitName = toCamelCase(circuit); const solidityFileTmp = `${solidityFile}.tmp`; const fileContent = fs.readFileSync(solidityFile, 'utf8'); - const updatedContent = fileContent.replace( - ' Groth16Verifier ', - ` Groth16Verifier_${camelCaseCircuitName} ` - ); + const updatedContent = fileContent.replace(' Groth16Verifier ', ` Groth16Verifier_${camelCaseCircuitName} `); fs.writeFileSync(solidityFileTmp, updatedContent); fs.renameSync(solidityFileTmp, solidityFile); }; const run = async () => { - if (specificCircuit) { - // if a specific circuit is provided, check it's in the map - if (!circuits[specificCircuit]) { - console.error(`Error: Unknown circuit: ${specificCircuit}`); - process.exit(1); + if (specificCircuits) { + // if specific circuits are provided, check it's in the map + for (const circuit of specificCircuits) { + if (!circuits[circuit]) { + console.error(`Error: Unknown circuit: ${circuit}`); + process.exit(1); + } } } @@ -138,7 +128,7 @@ const run = async () => { const activePromises = new Set(); for (const [circuit, { ptau, skipSolidityGenaration }] of circuitsArray) { - if (specificCircuit && circuit !== specificCircuit) { + if (specificCircuits && !specificCircuits.includes(circuit)) { continue; } diff --git a/zkp/circuits/package.json b/zkp/circuits/package.json index b818b77..4e61418 100644 --- a/zkp/circuits/package.json +++ b/zkp/circuits/package.json @@ -4,7 +4,8 @@ "description": "Circom library for ZK circuits for use with Zeto based privacy-preserving tokens", "license": "Apache-2.0", "dependencies": { - "circomlib": "^2.0.5" + "circomlib": "^2.0.5", + "yargs": "^17.7.2" }, "scripts": { "gen": "node gen.js" diff --git a/zkp/js/index.js b/zkp/js/index.js index f53673c..498d856 100644 --- a/zkp/js/index.js +++ b/zkp/js/index.js @@ -23,8 +23,12 @@ function loadCircuit(type) { if (!type) { throw new Error('The circuit name must be provided'); } - const WitnessCalculator = require(`./lib/${type}_js/witness_calculator.js`); - const buffer = readFileSync(path.join(__dirname, `./lib/${type}_js/${type}.wasm`)); + const circuitsRoot = process.env.CIRCUITS_ROOT; + if (!circuitsRoot) { + throw new Error('CIRCUITS_ROOT is not set'); + } + const WitnessCalculator = require(path.join(circuitsRoot, `${type}_js/witness_calculator.js`)); + const buffer = readFileSync(path.join(circuitsRoot, `${type}_js/${type}.wasm`)); return WitnessCalculator(buffer); } From a7f611a7689ec859869a0c0c43f5ae9817001b88 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 13 Aug 2024 12:47:23 -0400 Subject: [PATCH 02/10] Remove pkg from test coverage Signed-off-by: Jim Zhang --- go-sdk/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-sdk/Makefile b/go-sdk/Makefile index 323f3c5..32221ae 100644 --- a/go-sdk/Makefile +++ b/go-sdk/Makefile @@ -11,7 +11,7 @@ GOGC=30 all: test go-mod-tidy test: deps lint - $(VGO) test ./internal/... ./pkg/... -cover -coverprofile=coverage.txt -covermode=atomic -timeout=30s ${TEST_ARGS} + $(VGO) test ./internal/... -cover -coverprofile=coverage.txt -covermode=atomic -timeout=30s ${TEST_ARGS} coverage.html: $(VGO) tool cover -html=coverage.txt coverage: test coverage.html From fafec447f17cdc2d59b441fe74e7b9fe1111658b Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 13 Aug 2024 12:47:34 -0400 Subject: [PATCH 03/10] Update README Signed-off-by: Jim Zhang --- zkp/js/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/zkp/js/README.md b/zkp/js/README.md index 72d91d0..6130d69 100644 --- a/zkp/js/README.md +++ b/zkp/js/README.md @@ -29,6 +29,7 @@ npm i - set where you want to store the generated verification keys and the downloaded PTAU files ```console + export CIRCUITS_ROOT="$HOME/circuits" export PROVING_KEYS_ROOT="$HOME/proving-keys" export PTAU_DOWNLOAD_PATH="$HOME/Downloads" mkdir -p $PROVING_KEYS_ROOT $PTAU_DOWNLOAD_PATH From d5020be9614a5ee74bf02400618445c12c5caed7 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 13 Aug 2024 13:12:40 -0400 Subject: [PATCH 04/10] Move yargs to dev dependencies Signed-off-by: Jim Zhang --- zkp/circuits/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zkp/circuits/package.json b/zkp/circuits/package.json index 4e61418..8af366e 100644 --- a/zkp/circuits/package.json +++ b/zkp/circuits/package.json @@ -4,14 +4,14 @@ "description": "Circom library for ZK circuits for use with Zeto based privacy-preserving tokens", "license": "Apache-2.0", "dependencies": { - "circomlib": "^2.0.5", - "yargs": "^17.7.2" + "circomlib": "^2.0.5" }, "scripts": { "gen": "node gen.js" }, "devDependencies": { "axios": "^1.7.3", - "p-limit": "^6.1.0" + "p-limit": "^6.1.0", + "yargs": "^17.7.2" } } From a3025ed5a247b144fbffbc8e84cccab2a1df6b2a Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 13 Aug 2024 15:52:57 -0400 Subject: [PATCH 05/10] Remove unnecessary module replace Signed-off-by: Jim Zhang --- go.mod | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.mod b/go.mod index c95cc67..7127f10 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,3 @@ module github.com/hyperledger-labs/zeto go 1.22.5 - -replace github.com/hyperledger-labs/zeto/go-sdk v0.0.0 => ./zkp/golang From 83af476ace744f49ec73992f0c4dd055229a5131 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Wed, 14 Aug 2024 10:23:48 -0400 Subject: [PATCH 06/10] Fix handling single specific circuit Signed-off-by: Jim Zhang --- zkp/circuits/gen.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/zkp/circuits/gen.js b/zkp/circuits/gen.js index 51a2bc3..53e232d 100644 --- a/zkp/circuits/gen.js +++ b/zkp/circuits/gen.js @@ -7,9 +7,9 @@ const yargs = require('yargs/yargs'); const { hideBin } = require('yargs/helpers'); const argv = yargs(hideBin(process.argv)).argv; -const circuitsRoot = process.env.CIRCUITS_ROOT; -const provingKeysRoot = process.env.PROVING_KEYS_ROOT; -const ptauDownload = process.env.PTAU_DOWNLOAD_PATH; +const circuitsRoot = process.env.CIRCUITS_ROOT || argv.circuitsRoot; +const provingKeysRoot = process.env.PROVING_KEYS_ROOT || argv.provingKeysRoot; +const ptauDownload = process.env.PTAU_DOWNLOAD_PATH || argv.ptauDownloadPath; const specificCircuits = argv.c; const compileOnly = argv.compileOnly; const parallelLimit = parseInt(process.env.GEN_CONCURRENCY, 10) || 10; // Default to compile 10 circuits in parallel @@ -115,8 +115,13 @@ const processCircuit = async (circuit, ptau, skipSolidityGenaration) => { const run = async () => { if (specificCircuits) { + let onlyCircuits = specificCircuits; + if (!Array.isArray(specificCircuits)) { + onlyCircuits = [specificCircuits]; + } + // if specific circuits are provided, check it's in the map - for (const circuit of specificCircuits) { + for (const circuit of onlyCircuits) { if (!circuits[circuit]) { console.error(`Error: Unknown circuit: ${circuit}`); process.exit(1); From 6c4b9044d58862f73161e48629d9d127e8faa330 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Wed, 14 Aug 2024 10:52:33 -0400 Subject: [PATCH 07/10] Remove unreliable merkle tree delete calls Signed-off-by: Jim Zhang --- solidity/test/zeto_anon_enc_nullifier.ts | 4 -- solidity/test/zeto_anon_nullifier.ts | 4 -- solidity/test/zeto_anon_nullifier_kyc.ts | 75 +++++++++++------------- solidity/test/zeto_nf_anon_nullifier.ts | 3 - 4 files changed, 35 insertions(+), 51 deletions(-) diff --git a/solidity/test/zeto_anon_enc_nullifier.ts b/solidity/test/zeto_anon_enc_nullifier.ts index 584656a..9364f3d 100644 --- a/solidity/test/zeto_anon_enc_nullifier.ts +++ b/solidity/test/zeto_anon_enc_nullifier.ts @@ -280,10 +280,6 @@ describe("Zeto based fungible token with anonymity using nullifiers and encrypti utxo7 = newUTXO(15, Bob); await expect(doTransfer(Alice, [nonExisting1, nonExisting2], [nullifier1, nullifier2], [utxo7, _utxo1], root.bigInt(), merkleProofs, [Bob, Charlie])).rejectedWith("UTXORootNotFound"); - - // clean up the fake UTXOs from the local SMT - await smtAlice.delete(nonExisting1.hash); - await smtAlice.delete(nonExisting2.hash); }).timeout(600000); async function doTransfer(signer: User, inputs: UTXO[], _nullifiers: UTXO[], outputs: UTXO[], root: BigInt, merkleProofs: BigInt[][], owners: User[]) { diff --git a/solidity/test/zeto_anon_nullifier.ts b/solidity/test/zeto_anon_nullifier.ts index 996224f..95fd9ea 100644 --- a/solidity/test/zeto_anon_nullifier.ts +++ b/solidity/test/zeto_anon_nullifier.ts @@ -281,10 +281,6 @@ describe("Zeto based fungible token with anonymity using nullifiers without encr utxo7 = newUTXO(15, Bob); await expect(doTransfer(Alice, [nonExisting1, nonExisting2], [nullifier1, nullifier2], [utxo7, _utxo1], root.bigInt(), merkleProofs, [Bob, Charlie])).rejectedWith("UTXORootNotFound"); - - // clean up the fake UTXOs from the local SMT - await smtAlice.delete(nonExisting1.hash); - await smtAlice.delete(nonExisting2.hash); }).timeout(600000); async function doTransfer(signer: User, inputs: UTXO[], _nullifiers: UTXO[], outputs: UTXO[], root: BigInt, merkleProofs: BigInt[][], owners: User[]) { diff --git a/solidity/test/zeto_anon_nullifier_kyc.ts b/solidity/test/zeto_anon_nullifier_kyc.ts index 6fe3a38..448f1b2 100644 --- a/solidity/test/zeto_anon_nullifier_kyc.ts +++ b/solidity/test/zeto_anon_nullifier_kyc.ts @@ -327,46 +327,6 @@ describe("Zeto based fungible token with anonymity, KYC, using nullifiers withou await expect(doTransfer(Bob, [utxo7, utxo7], [nullifier1, nullifier2], [_utxo1, _utxo2], root.bigInt(), merkleProofs, identitiesRoot.bigInt(), identitiesMerkleProofs, [Alice, Bob])).rejectedWith(`UTXODuplicate`); }).timeout(600000); - - it("transfer non-existing UTXOs should fail", async function () { - const nonExisting1 = newUTXO(25, Alice); - const nonExisting2 = newUTXO(20, Alice, nonExisting1.salt); - - // add to our local SMT (but they don't exist on the chain) - await smtAlice.add(nonExisting1.hash, nonExisting1.hash); - await smtAlice.add(nonExisting2.hash, nonExisting2.hash); - - // generate the nullifiers for the UTXOs to be spent - const nullifier1 = newNullifier(nonExisting1, Alice); - const nullifier2 = newNullifier(nonExisting2, Alice); - - // generate inclusion proofs for the UTXOs to be spent - let root = await smtAlice.root(); - const proof1 = await smtAlice.generateCircomVerifierProof(nonExisting1.hash, root); - const proof2 = await smtAlice.generateCircomVerifierProof(nonExisting2.hash, root); - const merkleProofs = [proof1.siblings.map((s) => s.bigInt()), proof2.siblings.map((s) => s.bigInt())]; - - // propose the output UTXOs - const _utxo1 = newUTXO(30, Charlie); - utxo7 = newUTXO(15, Bob); - - const identitiesRoot = await smtKyc.root(); - const proof3 = await smtKyc.generateCircomVerifierProof(kycHash(Alice.babyJubPublicKey), identitiesRoot); - const proof4 = await smtKyc.generateCircomVerifierProof(kycHash(Bob.babyJubPublicKey), identitiesRoot); - const proof5 = await smtKyc.generateCircomVerifierProof(kycHash(Charlie.babyJubPublicKey), identitiesRoot); - const identitiesMerkleProofs = [ - 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) - proof5.siblings.map((s) => s.bigInt()) // identity proof for the 2nd owner of the output UTXO (Charlie) - ]; - - await expect(doTransfer(Alice, [nonExisting1, nonExisting2], [nullifier1, nullifier2], [utxo7, _utxo1], root.bigInt(), merkleProofs, identitiesRoot.bigInt(), identitiesMerkleProofs, [Bob, Charlie])).rejectedWith("UTXORootNotFound"); - - // clean up the fake UTXOs from the local SMT - await smtAlice.delete(nonExisting1.hash); - await smtAlice.delete(nonExisting2.hash); - }).timeout(600000); - it("transfer from an unregistered user should fail", async function () { const tx = await erc20.connect(deployer).mint(unregistered.ethAddress, 100); await tx.wait(); @@ -422,6 +382,41 @@ describe("Zeto based fungible token with anonymity, KYC, using nullifiers withou expect(balance).to.equal(100); }); + it("transfer non-existing UTXOs should fail", async function () { + const nonExisting1 = newUTXO(25, Alice); + const nonExisting2 = newUTXO(20, Alice, nonExisting1.salt); + + // add to our local SMT (but they don't exist on the chain) + await smtAlice.add(nonExisting1.hash, nonExisting1.hash); + await smtAlice.add(nonExisting2.hash, nonExisting2.hash); + + // generate the nullifiers for the UTXOs to be spent + const nullifier1 = newNullifier(nonExisting1, Alice); + const nullifier2 = newNullifier(nonExisting2, Alice); + + // generate inclusion proofs for the UTXOs to be spent + let root = await smtAlice.root(); + const proof1 = await smtAlice.generateCircomVerifierProof(nonExisting1.hash, root); + const proof2 = await smtAlice.generateCircomVerifierProof(nonExisting2.hash, root); + const merkleProofs = [proof1.siblings.map((s) => s.bigInt()), proof2.siblings.map((s) => s.bigInt())]; + + // propose the output UTXOs + const _utxo1 = newUTXO(30, Charlie); + utxo7 = newUTXO(15, Bob); + + const identitiesRoot = await smtKyc.root(); + const proof3 = await smtKyc.generateCircomVerifierProof(kycHash(Alice.babyJubPublicKey), identitiesRoot); + const proof4 = await smtKyc.generateCircomVerifierProof(kycHash(Bob.babyJubPublicKey), identitiesRoot); + const proof5 = await smtKyc.generateCircomVerifierProof(kycHash(Charlie.babyJubPublicKey), identitiesRoot); + const identitiesMerkleProofs = [ + 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) + proof5.siblings.map((s) => s.bigInt()) // identity proof for the 2nd owner of the output UTXO (Charlie) + ]; + + await expect(doTransfer(Alice, [nonExisting1, nonExisting2], [nullifier1, nullifier2], [utxo7, _utxo1], root.bigInt(), merkleProofs, identitiesRoot.bigInt(), identitiesMerkleProofs, [Bob, Charlie])).rejectedWith("UTXORootNotFound"); + }).timeout(600000); + async function doTransfer(signer: User, inputs: UTXO[], _nullifiers: UTXO[], outputs: UTXO[], utxosRoot: BigInt, utxosMerkleProofs: BigInt[][], identitiesRoot: BigInt, identitiesMerkleProof: BigInt[][], owners: User[]) { let nullifiers: [BigNumberish, BigNumberish]; let outputCommitments: [BigNumberish, BigNumberish]; diff --git a/solidity/test/zeto_nf_anon_nullifier.ts b/solidity/test/zeto_nf_anon_nullifier.ts index a212213..63871a7 100644 --- a/solidity/test/zeto_nf_anon_nullifier.ts +++ b/solidity/test/zeto_nf_anon_nullifier.ts @@ -182,9 +182,6 @@ describe("Zeto based non-fungible token with anonymity using nullifiers without const _utxo1 = newAssetUTXO(nonExisting1.tokenId!, nonExisting1.uri!, Charlie); await expect(doTransfer(Alice, nonExisting1, nullifier1, _utxo1, root.bigInt(), merkleProof, Charlie)).rejectedWith("UTXORootNotFound"); - - // clean up the fake UTXOs from the local SMT - await smtAlice.delete(nonExisting1.hash); }).timeout(600000); async function doTransfer(signer: User, input: UTXO, _nullifier: UTXO, output: UTXO, root: BigInt, merkleProof: BigInt[], owner: User) { From ab558893d90ce8a1f0d07da2c0f749a02e76fe9c Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Wed, 14 Aug 2024 12:02:51 -0400 Subject: [PATCH 08/10] Fix handling single circuit Signed-off-by: Jim Zhang --- zkp/circuits/gen.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zkp/circuits/gen.js b/zkp/circuits/gen.js index 53e232d..6ef742e 100644 --- a/zkp/circuits/gen.js +++ b/zkp/circuits/gen.js @@ -14,6 +14,8 @@ const specificCircuits = argv.c; const compileOnly = argv.compileOnly; const parallelLimit = parseInt(process.env.GEN_CONCURRENCY, 10) || 10; // Default to compile 10 circuits in parallel +console.log(argv); + // check env vars if (!circuitsRoot) { console.error('Error: CIRCUITS_ROOT is not set.'); @@ -114,8 +116,8 @@ const processCircuit = async (circuit, ptau, skipSolidityGenaration) => { }; const run = async () => { + let onlyCircuits = specificCircuits; if (specificCircuits) { - let onlyCircuits = specificCircuits; if (!Array.isArray(specificCircuits)) { onlyCircuits = [specificCircuits]; } @@ -133,7 +135,7 @@ const run = async () => { const activePromises = new Set(); for (const [circuit, { ptau, skipSolidityGenaration }] of circuitsArray) { - if (specificCircuits && !specificCircuits.includes(circuit)) { + if (onlyCircuits && !onlyCircuits.includes(circuit)) { continue; } From 0ac61338c5dc294d411fc7db8114634ab016740f Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Wed, 14 Aug 2024 12:13:02 -0400 Subject: [PATCH 09/10] Remove debug log Signed-off-by: Jim Zhang --- zkp/circuits/gen.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/zkp/circuits/gen.js b/zkp/circuits/gen.js index 6ef742e..d7c2017 100644 --- a/zkp/circuits/gen.js +++ b/zkp/circuits/gen.js @@ -14,8 +14,6 @@ const specificCircuits = argv.c; const compileOnly = argv.compileOnly; const parallelLimit = parseInt(process.env.GEN_CONCURRENCY, 10) || 10; // Default to compile 10 circuits in parallel -console.log(argv); - // check env vars if (!circuitsRoot) { console.error('Error: CIRCUITS_ROOT is not set.'); From 1f80f1fd808eaa55055ba7ff3aad2c0573b24fc4 Mon Sep 17 00:00:00 2001 From: Chengxuan Xing Date: Wed, 14 Aug 2024 18:58:21 +0100 Subject: [PATCH 10/10] some readme update Signed-off-by: Chengxuan Xing --- zkp/circuits/gen.js | 62 ++++++++++++++++++++++++++++++++++++++------- zkp/js/README.md | 4 +-- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/zkp/circuits/gen.js b/zkp/circuits/gen.js index d7c2017..7f8c9b3 100644 --- a/zkp/circuits/gen.js +++ b/zkp/circuits/gen.js @@ -30,6 +30,23 @@ if (!compileOnly && !ptauDownload) { process.exit(1); } +console.log( + 'Generating circuits with the following settings:\n' + + JSON.stringify( + { + specificCircuits, + compileOnly, + parallelLimit, + circuitsRoot, + provingKeysRoot, + ptauDownload, + }, + null, + 2 + ) + + '\n' +); + // load circuits const circuits = require('./gen-config.json'); @@ -66,9 +83,12 @@ const processCircuit = async (circuit, ptau, skipSolidityGenaration) => { if (!compileOnly && !fs.existsSync(ptauFile)) { log(circuit, `PTAU file does not exist, downloading: ${ptauFile}`); try { - const response = await axios.get(`https://storage.googleapis.com/zkevm/ptau/${ptau}.ptau`, { - responseType: 'stream', - }); + const response = await axios.get( + `https://storage.googleapis.com/zkevm/ptau/${ptau}.ptau`, + { + responseType: 'stream', + } + ); response.data.pipe(fs.createWriteStream(ptauFile)); await new Promise((resolve, reject) => { response.data.on('end', resolve); @@ -81,7 +101,9 @@ const processCircuit = async (circuit, ptau, skipSolidityGenaration) => { } log(circuit, `Compiling circuit`); - await execAsync(`circom ${circomInput} --output ${circuitsRoot} --sym --wasm`); + await execAsync( + `circom ${circomInput} --output ${circuitsRoot} --sym --wasm` + ); if (compileOnly) { return; } @@ -89,10 +111,20 @@ const processCircuit = async (circuit, ptau, skipSolidityGenaration) => { await execAsync(`circom ${circomInput} --output ${provingKeysRoot} --r1cs`); log(circuit, `Generating test proving key with ${ptau}`); - await execAsync(`snarkjs groth16 setup ${path.join(provingKeysRoot, `${circuit}.r1cs`)} ${ptauFile} ${zkeyOutput}`); + await execAsync( + `snarkjs groth16 setup ${path.join( + provingKeysRoot, + `${circuit}.r1cs` + )} ${ptauFile} ${zkeyOutput}` + ); log(circuit, `Generating verification key`); - await execAsync(`snarkjs zkey export verificationkey ${zkeyOutput} ${path.join(provingKeysRoot, `${circuit}-vkey.json`)}`); + await execAsync( + `snarkjs zkey export verificationkey ${zkeyOutput} ${path.join( + provingKeysRoot, + `${circuit}-vkey.json` + )}` + ); if (skipSolidityGenaration) { log(circuit, `Skipping solidity verifier generation`); @@ -100,15 +132,27 @@ const processCircuit = async (circuit, ptau, skipSolidityGenaration) => { } log(circuit, `Generating solidity verifier`); - const solidityFile = path.join('..', '..', 'solidity', 'contracts', 'lib', `verifier_${circuit}.sol`); - await execAsync(`snarkjs zkey export solidityverifier ${zkeyOutput} ${solidityFile}`); + const solidityFile = path.join( + '..', + '..', + 'solidity', + 'contracts', + 'lib', + `verifier_${circuit}.sol` + ); + await execAsync( + `snarkjs zkey export solidityverifier ${zkeyOutput} ${solidityFile}` + ); log(circuit, `Modifying the contract name in the Solidity file`); const camelCaseCircuitName = toCamelCase(circuit); const solidityFileTmp = `${solidityFile}.tmp`; const fileContent = fs.readFileSync(solidityFile, 'utf8'); - const updatedContent = fileContent.replace(' Groth16Verifier ', ` Groth16Verifier_${camelCaseCircuitName} `); + const updatedContent = fileContent.replace( + ' Groth16Verifier ', + ` Groth16Verifier_${camelCaseCircuitName} ` + ); fs.writeFileSync(solidityFileTmp, updatedContent); fs.renameSync(solidityFileTmp, solidityFile); }; diff --git a/zkp/js/README.md b/zkp/js/README.md index 6130d69..86bdaf5 100644 --- a/zkp/js/README.md +++ b/zkp/js/README.md @@ -32,13 +32,13 @@ npm i export CIRCUITS_ROOT="$HOME/circuits" export PROVING_KEYS_ROOT="$HOME/proving-keys" export PTAU_DOWNLOAD_PATH="$HOME/Downloads" - mkdir -p $PROVING_KEYS_ROOT $PTAU_DOWNLOAD_PATH + mkdir -p $PROVING_KEYS_ROOT $PTAU_DOWNLOAD_PATH $CIRCUITS_ROOT ``` - run the generation script for **ALL** circuits ```console npm run gen ``` - **run `npm run gen $circuit` for developing a single circuit** + **run `npm run gen -- -c $circuit` for developing a single circuit** **use `GEN_CONCURRENCY` to control how many circuits to be processed in parallel, default to 10** > Refer to [generation script explanation](#generation-script-explanation) for what the script does