From 960b6adb30733e35d53a2275911a89d11ac2602d Mon Sep 17 00:00:00 2001 From: poma Date: Tue, 15 Jun 2021 14:25:06 +0300 Subject: [PATCH] transaaction circuit with custom input count --- circuits/transaction.circom | 23 ++++++++++++++++------- contracts/TornadoPool.sol | 14 +++++++++----- contracts/Verifier.sol | 1 - contracts/Verifier16.sol | 1 + contracts/Verifier2.sol | 1 + package.json | 7 +++---- scripts/buildCircuit.sh | 15 ++++++++------- scripts/deploy.js | 15 ++++++++++----- test/full.test.js | 12 ++++++++---- yarn.lock | 27 +++++++++++++-------------- 10 files changed, 69 insertions(+), 47 deletions(-) delete mode 120000 contracts/Verifier.sol create mode 120000 contracts/Verifier16.sol create mode 120000 contracts/Verifier2.sol diff --git a/circuits/transaction.circom b/circuits/transaction.circom index 2fdc2c1..40cf27d 100644 --- a/circuits/transaction.circom +++ b/circuits/transaction.circom @@ -14,7 +14,7 @@ commitment = hash(amount, blinding, pubKey) nullifier = hash(commitment, privKey, merklePath) */ -// Universal JoinSplit transaction with 2 inputs and 2 outputs +// Universal JoinSplit transaction with nIns inputs and 2 outputs template Transaction(levels, nIns, nOuts, zeroLeaf) { signal input root; signal input newRoot; @@ -107,10 +107,18 @@ template Transaction(levels, nIns, nOuts, zeroLeaf) { component feeCheck = Num2Bits(248); feeCheck.in <== fee; - component sameNullifiers = IsEqual(); - sameNullifiers.in[0] <== inputNullifier[0]; - sameNullifiers.in[1] <== inputNullifier[1]; - sameNullifiers.out === 0; + // check that there are no same nullifiers among all inputs + component sameNullifiers[nIns * (nIns - 1) / 2]; + var index = 0; + for (var i = 0; i < nIns - 1; i++) { + for (var j = i + 1; j < nIns; j++) { + sameNullifiers[index] = IsEqual(); + sameNullifiers[index].in[0] <== inputNullifier[i]; + sameNullifiers[index].in[1] <== inputNullifier[j]; + sameNullifiers[index].out === 0; + index++; + } + } // verify amount invariant sumIns + extAmount === sumOuts + fee; @@ -119,8 +127,9 @@ template Transaction(levels, nIns, nOuts, zeroLeaf) { component treeUpdater = TreeUpdater(levels, zeroLeaf); treeUpdater.oldRoot <== root; treeUpdater.newRoot <== newRoot; - treeUpdater.leaf[0] <== outputCommitment[0]; - treeUpdater.leaf[1] <== outputCommitment[1]; + for (var i = 0; i < nOuts; i++) { + treeUpdater.leaf[i] <== outputCommitment[i]; + } treeUpdater.pathIndices <== outPathIndices; for (var i = 0; i < levels - 1; i++) { treeUpdater.pathElements[i] <== outPathElements[i]; diff --git a/contracts/TornadoPool.sol b/contracts/TornadoPool.sol index 1f7ebda..6cd3a24 100644 --- a/contracts/TornadoPool.sol +++ b/contracts/TornadoPool.sol @@ -17,6 +17,7 @@ import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; // todo: maybe remov interface IVerifier { function verifyProof(bytes memory _proof, uint256[9] memory _input) external returns(bool); + function verifyProof(bytes memory _proof, uint256[23] memory _input) external returns(bool); } contract TornadoPool is ReentrancyGuard { @@ -26,7 +27,8 @@ contract TornadoPool is ReentrancyGuard { mapping(bytes32 => bool) public nullifierHashes; bytes32 public currentRoot; uint public currentCommitmentIndex; - IVerifier public verifier; + IVerifier public verifier2; + IVerifier public verifier16; struct ExtData { address payable recipient; @@ -42,10 +44,12 @@ contract TornadoPool is ReentrancyGuard { /** @dev The constructor - @param _verifier the address of SNARK verifier for this contract + @param _verifier2 the address of SNARK verifier for this contract + @param _verifier16 the address of SNARK verifier for this contract */ - constructor(IVerifier _verifier, bytes32 _currentRoot) public { - verifier = _verifier; + constructor(IVerifier _verifier2, IVerifier _verifier16, bytes32 _currentRoot) public { + verifier2 = _verifier2; + verifier16 = _verifier16; currentRoot = _currentRoot; } @@ -66,7 +70,7 @@ contract TornadoPool is ReentrancyGuard { require(!isSpent(_inputNullifiers[0]), "Input 0 is already spent"); require(!isSpent(_inputNullifiers[1]), "Input 1 is already spent"); require(uint256(_extDataHash) == uint256(keccak256(abi.encode(_extData))) % FIELD_SIZE, "Incorrect external data hash"); - require(verifier.verifyProof(_proof, [ + require(verifier2.verifyProof(_proof, [ uint256(_root), uint256(_newRoot), uint256(_inputNullifiers[0]), diff --git a/contracts/Verifier.sol b/contracts/Verifier.sol deleted file mode 120000 index 69a3b3f..0000000 --- a/contracts/Verifier.sol +++ /dev/null @@ -1 +0,0 @@ -../artifacts/circuits/Verifier.sol \ No newline at end of file diff --git a/contracts/Verifier16.sol b/contracts/Verifier16.sol new file mode 120000 index 0000000..8c5020b --- /dev/null +++ b/contracts/Verifier16.sol @@ -0,0 +1 @@ +../artifacts/circuits/Verifier16.sol \ No newline at end of file diff --git a/contracts/Verifier2.sol b/contracts/Verifier2.sol new file mode 120000 index 0000000..54f1602 --- /dev/null +++ b/contracts/Verifier2.sol @@ -0,0 +1 @@ +../artifacts/circuits/Verifier2.sol \ No newline at end of file diff --git a/package.json b/package.json index 919d520..005b1ad 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,10 @@ "test": "test" }, "scripts": { - "circuit": "./scripts/buildCircuit.sh transaction2 && ./scripts/buildCircuit.sh transaction16", + "circuit": "./scripts/buildCircuit.sh 2 && ./scripts/buildCircuit.sh 16", "compile": "npx hardhat compile", "build": "npm run circuit && npm run compile", - "migrate": "npx hardhat run scripts/deploy.js --network localhost", - "start": "node ./src/index.js" + "test": "npx hardhat test" }, "keywords": [], "author": "", @@ -32,7 +31,7 @@ "ffjavascript": "^0.2.36", "fixed-merkle-tree": "^0.5.0", "hardhat": "^2.3.0", - "snarkjs": "^0.4.5", + "snarkjs": "git+https://github.com/tornadocash/snarkjs.git#c103e3bf10e95e2e9bbf0f7952ed13812f8e39d3", "tmp-promise": "^3.0.2" } } diff --git a/scripts/buildCircuit.sh b/scripts/buildCircuit.sh index c8bf8d1..a815ecd 100755 --- a/scripts/buildCircuit.sh +++ b/scripts/buildCircuit.sh @@ -8,10 +8,11 @@ if [ ! -f artifacts/circuits/ptau$POWERS_OF_TAU ]; then npx snarkjs powersoftau prepare phase2 artifacts/circuits/tmp2_ptau$POWERS_OF_TAU artifacts/circuits/ptau$POWERS_OF_TAU rm artifacts/circuits/tmp_ptau$POWERS_OF_TAU artifacts/circuits/tmp2_ptau$POWERS_OF_TAU fi -npx circom -v -r artifacts/circuits/$1.r1cs -w artifacts/circuits/$1.wasm -s artifacts/circuits/$1.sym circuits/$1.circom -npx snarkjs groth16 setup artifacts/circuits/$1.r1cs artifacts/circuits/ptau$POWERS_OF_TAU artifacts/circuits/tmp_$1.zkey -npx snarkjs zkey contribute artifacts/circuits/tmp_$1.zkey artifacts/circuits/$1.zkey -npx snarkjs zkey export solidityverifier artifacts/circuits/$1.zkey artifacts/circuits/Verifier.sol -#zkutil setup -c artifacts/circuits/$1.r1cs -p artifacts/circuits/$1.params -#zkutil generate-verifier -p artifacts/circuits/$1.params -v artifacts/circuits/Verifier.sol -npx snarkjs info -r artifacts/circuits/$1.r1cs +npx circom -v -r artifacts/circuits/transaction$1.r1cs -w artifacts/circuits/transaction$1.wasm -s artifacts/circuits/transaction$1.sym circuits/transaction$1.circom +npx snarkjs groth16 setup artifacts/circuits/transaction$1.r1cs artifacts/circuits/ptau$POWERS_OF_TAU artifacts/circuits/tmp_transaction$1.zkey +npx snarkjs zkey contribute artifacts/circuits/tmp_transaction$1.zkey artifacts/circuits/transaction$1.zkey +npx snarkjs zkey export solidityverifier artifacts/circuits/transaction$1.zkey artifacts/circuits/Verifier$1.sol +sed -i.bak "s/contract Verifier/contract Verifier${1}/g" artifacts/circuits/Verifier$1.sol +#zkutil setup -c artifacts/circuits/transaction$1.r1cs -p artifacts/circuits/transaction$1.params +#zkutil generate-verifier -p artifacts/circuits/transaction$1.params -v artifacts/circuits/Verifier.sol +npx snarkjs info -r artifacts/circuits/transaction$1.r1cs diff --git a/scripts/deploy.js b/scripts/deploy.js index d734933..92c64a0 100644 --- a/scripts/deploy.js +++ b/scripts/deploy.js @@ -14,16 +14,21 @@ const toFixedHex = (number, length = 32) => ).padStart(length * 2, '0') async function main() { - const Verifier = await ethers.getContractFactory('Verifier') - const verifier = await Verifier.deploy() - await verifier.deployed() - console.log(`verifier: ${verifier.address}`) + const Verifier2 = await ethers.getContractFactory('Verifier2') + const verifier2 = await Verifier2.deploy() + await verifier2.deployed() + console.log(`verifier2: ${verifier2.address}`) + + const Verifier16 = await ethers.getContractFactory('Verifier16') + const verifier16 = await Verifier16.deploy() + await verifier16.deployed() + console.log(`verifier16: ${verifier16.address}`) const tree = new MerkleTree(MERKLE_TREE_HEIGHT, [], { hashFunction: poseidonHash2 }) const root = await tree.root() const Pool = await ethers.getContractFactory('TornadoPool') - const tornado = await Pool.deploy(verifier.address, toFixedHex(root)) + const tornado = await Pool.deploy(verifier2.address, verifier16.address, toFixedHex(root)) console.log("TornadoPool's address ", tornado.address) } diff --git a/test/full.test.js b/test/full.test.js index d1f2291..90616b7 100644 --- a/test/full.test.js +++ b/test/full.test.js @@ -14,15 +14,19 @@ describe('TornadoPool', () => { /* prettier-ignore */ before(async function () { - const Verifier = await ethers.getContractFactory('Verifier') - const verifier = await Verifier.deploy() - await verifier.deployed() + const Verifier2 = await ethers.getContractFactory('Verifier2') + const verifier2 = await Verifier2.deploy() + await verifier2.deployed() + + const Verifier16 = await ethers.getContractFactory('Verifier16') + const verifier16 = await Verifier16.deploy() + await verifier16.deployed() const tree = new MerkleTree(MERKLE_TREE_HEIGHT, [], { hashFunction: poseidonHash2 }) const root = await tree.root() const Pool = await ethers.getContractFactory('TornadoPool') - tornadoPool = await Pool.deploy(verifier.address, toFixedHex(root)) + tornadoPool = await Pool.deploy(verifier2.address, verifier16.address, toFixedHex(root)) snapshotId = await takeSnapshot() }) diff --git a/yarn.lock b/yarn.lock index c1bd4b5..7d8976e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7423,10 +7423,20 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -snarkjs@^0.4.5: +"snarkjs@git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5": + version "0.1.20" + resolved "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5" + dependencies: + big-integer "^1.6.43" + chai "^4.2.0" + escape-string-regexp "^1.0.5" + eslint "^5.16.0" + keccak "^2.0.0" + yargs "^12.0.5" + +"snarkjs@git+https://github.com/tornadocash/snarkjs.git#c103e3bf10e95e2e9bbf0f7952ed13812f8e39d3": version "0.4.5" - resolved "https://registry.yarnpkg.com/snarkjs/-/snarkjs-0.4.5.tgz#c7246b0bdcdafd25c67c0ecc395556715b059a14" - integrity sha512-rgxbp3JMhGdPgkhCrssq+a4Bv2vm2QucWwK9QG5cdyRRpx8f5EOpyMPy7pi/U8VUyyyulAaDowKBf7x7chB7zg== + resolved "git+https://github.com/tornadocash/snarkjs.git#c103e3bf10e95e2e9bbf0f7952ed13812f8e39d3" dependencies: "@iden3/binfileutils" "0.0.8" blake2b-wasm "https://github.com/jbaylina/blake2b-wasm.git" @@ -7439,17 +7449,6 @@ snarkjs@^0.4.5: r1csfile "0.0.32" readline "^1.3.0" -"snarkjs@git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5": - version "0.1.20" - resolved "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5" - dependencies: - big-integer "^1.6.43" - chai "^4.2.0" - escape-string-regexp "^1.0.5" - eslint "^5.16.0" - keccak "^2.0.0" - yargs "^12.0.5" - solc@0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a"