diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index dd4db64..f4b4744 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,6 +20,6 @@ jobs: run: npm install - name: Execute tests run: | - sh ./compile-circuits.sh + sh ./test-compile-circuits.sh npm run test-circuits npm run test-sc diff --git a/README.md b/README.md index 0dedb30..33792db 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ *From Esperanto, **miksi** (miksĀ·i): to mingle, to blend, to mix, to shuffle* Ethereum mixer where all the computation & constructions are done offchain and then proved inside a zkSNARK to the Smart Contract (for the *deposit* and for the *withdraw*). + This means that the client builds a MerkleTree and makes all the needed computation, and then generates a zk-proof where proves that all the offchain computation is done following all the rules (no leaf deletion, only one leaf addition, correct leaf format). This allows to use only `~325.000 gas` for the *deposit*, and `~308.000 gas` for the withdraw. diff --git a/circuits/deposit.circom b/circuits/deposit.circom index 9478276..4f5c6ad 100644 --- a/circuits/deposit.circom +++ b/circuits/deposit.circom @@ -107,4 +107,4 @@ template Deposit(nLevels) { smtNew.value <== hash.out; } -component main = Deposit(17); // 16 real levels (due circom leaf protection) +/* component main = Deposit(17); // 16 real levels (due circom leaf protection) */ diff --git a/circuits/main/deposit.circom b/circuits/main/deposit.circom new file mode 100644 index 0000000..4b9373c --- /dev/null +++ b/circuits/main/deposit.circom @@ -0,0 +1,2 @@ +include "../deposit.circom"; +component main = Deposit(17); // 16 real levels (due circom leaf protection) diff --git a/circuits/main/withdraw.circom b/circuits/main/withdraw.circom new file mode 100644 index 0000000..1ef2a6d --- /dev/null +++ b/circuits/main/withdraw.circom @@ -0,0 +1,3 @@ +include "../withdraw.circom"; + +component main = Withdraw(17); // 16 real levels (due circom leaf protection) diff --git a/circuits/withdraw.circom b/circuits/withdraw.circom index 0fcf465..73cc57a 100644 --- a/circuits/withdraw.circom +++ b/circuits/withdraw.circom @@ -62,4 +62,4 @@ template Withdraw(nLevels) { smtV.value <== hash.out; } -component main = Withdraw(17); // 16 real levels (due circom leaf protection) +/* component main = Withdraw(17); // 16 real levels (due circom leaf protection) */ diff --git a/compile-circuits.sh b/compile-circuits.sh index ebced55..ee1e8c7 100755 --- a/compile-circuits.sh +++ b/compile-circuits.sh @@ -6,9 +6,9 @@ mkdir build cd build compile_and_ts() { - echo $(date +"%T") "circom ../circuits/$CIRCUIT.circom --r1cs --wasm --sym" + echo $(date +"%T") "circom ../circuits/main/$CIRCUIT.circom --r1cs --wasm --sym" itime="$(date -u +%s)" - ../node_modules/.bin/circom ../circuits/$CIRCUIT.circom --r1cs --wasm --sym + ../node_modules/.bin/circom ../circuits/main/$CIRCUIT.circom --r1cs --wasm --sym ftime="$(date -u +%s)" echo " ($(($(date -u +%s)-$itime))s)" diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index b68c5ef..45d9bbf 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -1,5 +1,5 @@ -const DepositVerifier = artifacts.require("../contracts/DepositVerifier"); -const WithdrawVerifier = artifacts.require("../contracts/WithdrawVerifier"); +const DepositVerifier = artifacts.require("../test/build/DepositVerifier"); +const WithdrawVerifier = artifacts.require("../test/build/WithdrawVerifier"); module.exports = function(deployer) { deployer.deploy(DepositVerifier); diff --git a/test-compile-circuits.sh b/test-compile-circuits.sh new file mode 100755 index 0000000..ee51883 --- /dev/null +++ b/test-compile-circuits.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +# This file compiles the circuits to generate the test files + +# npm install +cd test +rm -r build +mkdir build +cd build + +compile_and_ts() { + echo $(date +"%T") "circom ../circuits/main/$CIRCUIT.circom --r1cs --wasm --sym" + itime="$(date -u +%s)" + ../../node_modules/.bin/circom ../circuits/main/$CIRCUIT.circom --r1cs --wasm --sym + ftime="$(date -u +%s)" + echo " ($(($(date -u +%s)-$itime))s)" + + echo $(date +"%T") "snarkjs info -r $CIRCUIT.r1cs" + ../../node_modules/.bin/snarkjs info -r $CIRCUIT.r1cs + + echo $(date +"%T") "snarkjs setup" + itime="$(date -u +%s)" + ../../node_modules/.bin/snarkjs setup -r $CIRCUIT.r1cs --pk $CIRCUIT-proving_key.json --vk $CIRCUIT-verification_key.json + echo " ($(($(date -u +%s)-$itime))s)" + echo $(date +"%T") "trusted setup generated" + + # sed -i 's/null/["0","0","0"]/g' proving_key.json + + + echo $(date +"%T") "snarkjs generateverifier" + itime="$(date -u +%s)" + ../../node_modules/.bin/snarkjs generateverifier --vk $CIRCUIT-verification_key.json -v $CIRCUIT-verifier.sol + echo " ($(($(date -u +%s)-$itime))s)" + echo $(date +"%T") "generateverifier generated" + + sed -i "s/solidity ^0.5.0/solidity ^0.6.0/g" ${CIRCUIT}-verifier.sol + sed -i "s/gas/gas()/g" ${CIRCUIT}-verifier.sol + sed -i "s/return the sum/return r the sum/g" ${CIRCUIT}-verifier.sol + sed -i "s/return the product/return r the product/g" ${CIRCUIT}-verifier.sol + sed -i "s/contract Verifier/contract ${CONTRACT}Verifier/g" ${CIRCUIT}-verifier.sol + sed -i "s/Pairing/${CONTRACT}Pairing/g" ${CIRCUIT}-verifier.sol + # cp ${CIRCUIT}-verifier.sol ../contracts/ + + node ../../node_modules/wasmsnark/tools/buildpkey.js -i ${CIRCUIT}-proving_key.json -o ${CIRCUIT}-proving_key.bin +} + +CIRCUIT="deposit" +CONTRACT="Deposit" +compile_and_ts +CIRCUIT="withdraw" +CONTRACT="Withdraw" +compile_and_ts + +cp ../../contracts/Miksi.sol ./Miksi.sol +cp -r ../../contracts/helpers ./helpers diff --git a/test/circuits/deposit.test.ts b/test/circuits/deposit.test.ts index 443a6ad..ada97b2 100644 --- a/test/circuits/deposit.test.ts +++ b/test/circuits/deposit.test.ts @@ -13,11 +13,11 @@ describe("deposit test", function () { it("Test Deposit", async () => { const circuit = await tester( - path.join(__dirname, "../../circuits", "deposit.circom"), + path.join(__dirname, "main", "deposit.circom"), {reduceConstraints: false} ); - const nLevels = 17; + const nLevels = 4; const secret = "1234567890"; const coinCode = "0"; diff --git a/test/circuits/main/deposit.circom b/test/circuits/main/deposit.circom new file mode 100644 index 0000000..d6cdded --- /dev/null +++ b/test/circuits/main/deposit.circom @@ -0,0 +1,3 @@ +include "../../../circuits/deposit.circom"; + +component main = Deposit(4); diff --git a/test/circuits/main/withdraw.circom b/test/circuits/main/withdraw.circom new file mode 100644 index 0000000..2cf7f9d --- /dev/null +++ b/test/circuits/main/withdraw.circom @@ -0,0 +1,3 @@ +include "../../../circuits/withdraw.circom"; + +component main = Withdraw(4); diff --git a/test/circuits/withdraw.test.ts b/test/circuits/withdraw.test.ts index 2915263..65c9adc 100644 --- a/test/circuits/withdraw.test.ts +++ b/test/circuits/withdraw.test.ts @@ -13,11 +13,11 @@ describe("withdraw test", function () { it("Test Withdraw", async () => { const circuit = await tester( - path.join(__dirname, "../../circuits", "withdraw.circom"), + path.join(__dirname, "main", "withdraw.circom"), {reduceConstraints: false} ); - const nLevels = 17; + const nLevels = 4; const secret = "1234567890"; const coinCode = "0"; diff --git a/test/contracts/miksi.test.ts b/test/contracts/miksi.test.ts index 6b98aef..e379725 100644 --- a/test/contracts/miksi.test.ts +++ b/test/contracts/miksi.test.ts @@ -1,6 +1,6 @@ -const DepositVerifier = artifacts.require("../../contracts/DepositVerifier"); -const WithdrawVerifier = artifacts.require("../../contracts/WithdrawVerifier"); -const Miksi = artifacts.require("../../contracts/Miksi.sol"); +const DepositVerifier = artifacts.require("../build/DepositVerifier"); +const WithdrawVerifier = artifacts.require("../build/WithdrawVerifier"); +const Miksi = artifacts.require("../build/Miksi.sol"); const chai = require("chai"); const expect = chai.expect; @@ -16,7 +16,7 @@ const smt = require("circomlib").smt; let insVerifier; let insMiksi; -const nLevels = 17; +const nLevels = 4; const secret = ["1234567890", "987654321", "123"]; const coinCode = "0"; // refearing to ETH @@ -105,7 +105,6 @@ contract("miksi", (accounts) => { }); it("Calculate witness and generate the zkProof", async () => { - this.timeout(10000000); await genZKProof(0, addr2, "1"); await genZKProof(1, addr4, "2"); await genZKProof(2, addr4, "3"); @@ -193,7 +192,7 @@ async function makeDeposit(u, addr) { }; // calculate witness - const wasm = await fs.promises.readFile("./build/deposit.wasm"); + const wasm = await fs.promises.readFile("./test/build/deposit.wasm"); const input = unstringifyBigInts({ "coinCode": coinCode, "amount": amount, @@ -215,14 +214,14 @@ async function makeDeposit(u, addr) { const witness = unstringifyBigInts(stringifyBigInts(w)); // generate zkproof of commitment using snarkjs (as is a test) - const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/deposit-proving_key.json", "utf8"))); + const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./test/build/deposit-proving_key.json", "utf8"))); // console.log("Generate zkSNARK proof"); const res = groth.genProof(provingKey, witness); proof[u] = res.proof; publicSignals[u] = res.publicSignals; - const verificationKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/deposit-verification_key.json", "utf8"))); + const verificationKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./test/build/deposit-verification_key.json", "utf8"))); let pubI = unstringifyBigInts([coinCode, amount, rootOld[u].toString(), rootNew[u].toString(), commitment[u], currKey]); let validCheck = groth.isValid(verificationKey, proof[u], pubI); assert(validCheck); @@ -250,7 +249,7 @@ async function genZKProof(u, addr, k) { // console.log("siblings", siblings); // calculate witness - const wasm = await fs.promises.readFile("./build/withdraw.wasm"); + const wasm = await fs.promises.readFile("./test/build/withdraw.wasm"); const input = unstringifyBigInts({ "coinCode": coinCode, "amount": amount, @@ -268,7 +267,7 @@ async function genZKProof(u, addr, k) { const witness = unstringifyBigInts(stringifyBigInts(w)); // generate zkproof of commitment using snarkjs (as is a test) - const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/withdraw-proving_key.json", "utf8"))); + const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./test/build/withdraw-proving_key.json", "utf8"))); // console.log("Generate zkSNARK proof"); const res = groth.genProof(provingKey, witness); diff --git a/truffle-config.js b/truffle-config.js index 1483520..bffed2e 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -18,6 +18,7 @@ module.exports = { enableTimeouts: false, before_timeout: 1000000000, test_timeout: 1000000000, + contracts_directory: "./test/build", compilers: { solc: { version: "0.6.0"