From 71efcb28a74bd94a9e0aa1e36bca2ce22466ab5b Mon Sep 17 00:00:00 2001 From: poma Date: Tue, 2 Feb 2021 14:20:59 +0300 Subject: [PATCH] hardhat fixes --- contracts/TornadoTrees.sol | 4 +- .../verifiers/BatchTreeUpdateVerifier.sol | 171 +--------------- scripts/buildCircuit.sh | 13 +- src/controller.js | 30 +-- src/utils.js | 146 +------------- test/pack.test.js | 92 +++------ test/sample-test.js | 14 -- test/snark.test.js | 6 +- test/tornadoTrees.test.js | 190 +++++++----------- 9 files changed, 137 insertions(+), 529 deletions(-) mode change 100644 => 120000 contracts/verifiers/BatchTreeUpdateVerifier.sol delete mode 100644 test/sample-test.js diff --git a/contracts/TornadoTrees.sol b/contracts/TornadoTrees.sol index 79821be..09c75d6 100644 --- a/contracts/TornadoTrees.sol +++ b/contracts/TornadoTrees.sol @@ -16,8 +16,8 @@ contract TornadoTrees is ITornadoTrees, EnsResolve { address public tornadoProxy; IVerifier public immutable treeUpdateVerifier; - // make sure CHUNK_TREE_HEIGHT has the same value in BatchTreeUpdate.circom and IVerifier.sol - uint256 public constant CHUNK_TREE_HEIGHT = 7; + // make sure CHUNK_TREE_HEIGHT has the same value in BatchTreeUpdate.circom + uint256 public constant CHUNK_TREE_HEIGHT = 2; uint256 public constant CHUNK_SIZE = 2**CHUNK_TREE_HEIGHT; uint256 public constant ITEM_SIZE = 32 + 20 + 4; uint256 public constant BYTES_SIZE = 32 + 32 + 4 + CHUNK_SIZE * ITEM_SIZE; diff --git a/contracts/verifiers/BatchTreeUpdateVerifier.sol b/contracts/verifiers/BatchTreeUpdateVerifier.sol deleted file mode 100644 index d2eef74..0000000 --- a/contracts/verifiers/BatchTreeUpdateVerifier.sol +++ /dev/null @@ -1,170 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.0; - -library Pairing { - uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - - struct G1Point { - uint256 X; - uint256 Y; - } - - // Encoding of field elements is: X[0] * z + X[1] - struct G2Point { - uint256[2] X; - uint256[2] Y; - } - - /* - * @return The negation of p, i.e. p.plus(p.negate()) should be zero - */ - function negate(G1Point memory p) internal pure returns (G1Point memory) { - // The prime q in the base field F_q for G1 - if (p.X == 0 && p.Y == 0) { - return G1Point(0, 0); - } else { - return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q)); - } - } - - /* - * @return r the sum of two points of G1 - */ - function plus( - G1Point memory p1, - G1Point memory p2 - ) internal view returns (G1Point memory r) { - uint256[4] memory input = [ - p1.X, p1.Y, - p2.X, p2.Y - ]; - bool success; - - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) - // Use "invalid" to make gas estimation work - switch success case 0 { invalid() } - } - - require(success, "pairing-add-failed"); - } - - /* - * @return r the product of a point on G1 and a scalar, i.e. - * p == p.scalarMul(1) and p.plus(p) == p.scalarMul(2) for all - * points p. - */ - function scalarMul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) { - uint256[3] memory input = [p.X, p.Y, s]; - bool success; - - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) - // Use "invalid" to make gas estimation work - switch success case 0 { invalid() } - } - - require(success, "pairing-mul-failed"); - } - - /* @return The result of computing the pairing check - * e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 - * For example, - * pairing([P1(), P1().negate()], [P2(), P2()]) should return true. - */ - function pairing( - G1Point memory a1, - G2Point memory a2, - G1Point memory b1, - G2Point memory b2, - G1Point memory c1, - G2Point memory c2, - G1Point memory d1, - G2Point memory d2 - ) internal view returns (bool) { - uint256[24] memory input = [ - a1.X, a1.Y, a2.X[0], a2.X[1], a2.Y[0], a2.Y[1], - b1.X, b1.Y, b2.X[0], b2.X[1], b2.Y[0], b2.Y[1], - c1.X, c1.Y, c2.X[0], c2.X[1], c2.Y[0], c2.Y[1], - d1.X, d1.Y, d2.X[0], d2.X[1], d2.Y[0], d2.Y[1] - ]; - uint256[1] memory out; - bool success; - - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 8, input, mul(24, 0x20), out, 0x20) - // Use "invalid" to make gas estimation work - switch success case 0 { invalid() } - } - - require(success, "pairing-opcode-failed"); - return out[0] != 0; - } -} - -contract BatchTreeUpdateVerifier { - uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - using Pairing for *; - - struct VerifyingKey { - Pairing.G1Point alfa1; - Pairing.G2Point beta2; - Pairing.G2Point gamma2; - Pairing.G2Point delta2; - Pairing.G1Point[2] IC; - } - - function verifyingKey() internal pure returns (VerifyingKey memory vk) { - vk.alfa1 = Pairing.G1Point(uint256(20475789791681002364587166738311620805815985969091106757478379420262430093495), uint256(3034180384279528157431123624668892018871098425968640214767822771352219138078)); - vk.beta2 = Pairing.G2Point([uint256(347992840312110670849483472224503623225781749273259516677464742758581199694), uint256(16853081403278411985324640353650047676779142117029386935051386044282804346484)], [uint256(10461241566647602546027012417757263991485755060136522105605550609788790829933), uint256(16049761706815422591462572571571264938897676292217555774707799384732883004386)]); - vk.gamma2 = Pairing.G2Point([uint256(5535450215937949788522672716791294482208969162172756729752675877422249461391), uint256(4537903555000997751027892507073556632992848536024556182449526590439971414042)], [uint256(6688278057604431581483695896713912024597719708930089928002132340517626404891), uint256(15745439923152020754042431613052318298038129099865656040309120795605091105487)]); - vk.delta2 = Pairing.G2Point([uint256(10712491908603553476637447918495381165104059355722416702328240143919146641319), uint256(15855442659923189569787773688895011287546687523233653745264460947047886121140)], [uint256(18278088599243830423965796542892879791365910862597475788753708589843343437901), uint256(10765606859348375283724614934374540130725132299795942405716724739350245709734)]); - vk.IC[0] = Pairing.G1Point(uint256(18147360875100520747353841225428915644191762631193821400291387675910597374366), uint256(17222433096548585553756828362569506045947134360392537102794184064340219776032)); - vk.IC[1] = Pairing.G1Point(uint256(3514632146136652297064638325657684436433185732623721288055192259268961814948), uint256(8363257337389338977321440370428118205387545635573906956020792115766452976369)); - - } - - /* - * @returns Whether the proof is valid given the hardcoded verifying key - * above and the public inputs - */ - function verifyProof( - bytes memory proof, - uint256[1] memory input - ) public view returns (bool) { - uint256[8] memory p = abi.decode(proof, (uint256[8])); - for (uint8 i = 0; i < p.length; i++) { - // Make sure that each element in the proof is less than the prime q - require(p[i] < PRIME_Q, "verifier-proof-element-gte-prime-q"); - } - Pairing.G1Point memory proofA = Pairing.G1Point(p[0], p[1]); - Pairing.G2Point memory proofB = Pairing.G2Point([p[2], p[3]], [p[4], p[5]]); - Pairing.G1Point memory proofC = Pairing.G1Point(p[6], p[7]); - - VerifyingKey memory vk = verifyingKey(); - // Compute the linear combination vkX - Pairing.G1Point memory vkX = vk.IC[0]; - for (uint256 i = 0; i < input.length; i++) { - // Make sure that every input is less than the snark scalar field - require(input[i] < SNARK_SCALAR_FIELD, "verifier-input-gte-snark-scalar-field"); - vkX = Pairing.plus(vkX, Pairing.scalarMul(vk.IC[i + 1], input[i])); - } - - return Pairing.pairing( - Pairing.negate(proofA), - proofB, - vk.alfa1, - vk.beta2, - vkX, - vk.gamma2, - proofC, - vk.delta2 - ); - } -} - diff --git a/contracts/verifiers/BatchTreeUpdateVerifier.sol b/contracts/verifiers/BatchTreeUpdateVerifier.sol new file mode 120000 index 0000000..f1c4139 --- /dev/null +++ b/contracts/verifiers/BatchTreeUpdateVerifier.sol @@ -0,0 +1 @@ +../../artifacts/circuits/BatchTreeUpdateVerifier.sol \ No newline at end of file diff --git a/scripts/buildCircuit.sh b/scripts/buildCircuit.sh index f95a2b7..67316d3 100755 --- a/scripts/buildCircuit.sh +++ b/scripts/buildCircuit.sh @@ -1,10 +1,11 @@ #!/bin/bash -e +mkdir -p artifacts/circuits if [ "$2" = "large" ]; then - npx circom -v -f -r build/circuits/$1.r1cs -c build/circuits/$1.cpp -s build/circuits/$1.sym circuits/$1.circom + npx circom -v -f -r artifacts/circuits/$1.r1cs -c artifacts/circuits/$1.cpp -s artifacts/circuits/$1.sym circuits/$1.circom else - npx circom -v -r build/circuits/$1.r1cs -w build/circuits/$1.wasm -s build/circuits/$1.sym circuits/$1.circom + npx circom -v -r artifacts/circuits/$1.r1cs -w artifacts/circuits/$1.wasm -s artifacts/circuits/$1.sym circuits/$1.circom fi -zkutil setup -c build/circuits/$1.r1cs -p build/circuits/$1.params -zkutil generate-verifier -p build/circuits/$1.params -v build/circuits/${1}Verifier.sol -sed -i.bak "s/contract Verifier/contract ${1}Verifier/g" build/circuits/${1}Verifier.sol -npx snarkjs info -r build/circuits/$1.r1cs +zkutil setup -c artifacts/circuits/$1.r1cs -p artifacts/circuits/$1.params +zkutil generate-verifier -p artifacts/circuits/$1.params -v artifacts/circuits/${1}Verifier.sol +sed -i.bak "s/contract Verifier/contract ${1}Verifier/g" artifacts/circuits/${1}Verifier.sol +npx snarkjs info -r artifacts/circuits/$1.r1cs diff --git a/src/controller.js b/src/controller.js index 70dc74f..403650d 100644 --- a/src/controller.js +++ b/src/controller.js @@ -1,11 +1,15 @@ +const ethers = require('ethers') +const BigNumber = ethers.BigNumber + const { bitsToNumber, toFixedHex, + toBuffer, poseidonHash, poseidonHash2, } = require('./utils') + const jsSHA = require('jssha') -const { toBN } = require('web3-utils') const fs = require('fs') const tmp = require('tmp-promise') @@ -14,18 +18,18 @@ const exec = util.promisify(require('child_process').exec) function hashInputs(input) { const sha = new jsSHA('SHA-256', 'ARRAYBUFFER') - sha.update(toBN(input.oldRoot).toBuffer('be', 32)) - sha.update(toBN(input.newRoot).toBuffer('be', 32)) - sha.update(toBN(input.pathIndices).toBuffer('be', 4)) + sha.update(toBuffer(input.oldRoot, 32)) + sha.update(toBuffer(input.newRoot, 32)) + sha.update(toBuffer(input.pathIndices, 4)) for (let i = 0; i < input.instances.length; i++) { - sha.update(toBN(input.hashes[i]).toBuffer('be', 32)) - sha.update(toBN(input.instances[i]).toBuffer('be', 20)) - sha.update(toBN(input.blocks[i]).toBuffer('be', 4)) + sha.update(toBuffer(input.hashes[i], 32)) + sha.update(toBuffer(input.instances[i], 20)) + sha.update(toBuffer(input.blocks[i], 4)) } - const hash = sha.getHash('HEX') - const result = toBN(hash).mod(toBN('21888242871839275222246405745257275088548364400416034343698204186575808495617')).toString() + const hash = '0x' + sha.getHash('HEX') + const result = BigNumber.from(hash).mod(BigNumber.from('21888242871839275222246405745257275088548364400416034343698204186575808495617')).toString() return result } @@ -63,7 +67,7 @@ function batchTreeUpdate(tree, events) { tree.bulkInsert(leaves) const newRoot = tree.root().toString() let { pathElements, pathIndices } = tree.path(tree.elements().length - 1) - pathElements = pathElements.slice(batchHeight).map(a => toBN(a).toString()) + pathElements = pathElements.slice(batchHeight).map(a => BigNumber.from(a).toString()) pathIndices = bitsToNumber(pathIndices.slice(batchHeight)).toString() const input = { @@ -71,9 +75,9 @@ function batchTreeUpdate(tree, events) { newRoot, pathIndices, pathElements, - instances: events.map((e) => toBN(e.instance).toString()), - hashes: events.map((e) => toBN(e.hash).toString()), - blocks: events.map((e) => toBN(e.block).toString()), + instances: events.map((e) => BigNumber.from(e.instance).toString()), + hashes: events.map((e) => BigNumber.from(e.hash).toString()), + blocks: events.map((e) => BigNumber.from(e.block).toString()), } input.argsHash = hashInputs(input) diff --git a/src/utils.js b/src/utils.js index 886c90c..8e9d4a3 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,119 +1,21 @@ const crypto = require('crypto') -const Decimal = require('decimal.js') -const { bigInt } = require('snarkjs') -const { toBN, BN, soliditySha3 } = require('web3-utils') -const Web3 = require('web3') -const web3 = new Web3() -const { babyJub, pedersenHash, mimcsponge, poseidon } = require('circomlib') +const ethers = require('ethers') +const BigNumber = ethers.BigNumber +const { poseidon } = require('circomlib') -const RewardExtData = { - RewardExtData: { - relayer: 'address', - encryptedAccount: 'bytes', - }, -} -const AccountUpdate = { - AccountUpdate: { - inputRoot: 'bytes32', - inputNullifierHash: 'bytes32', - outputRoot: 'bytes32', - outputPathIndices: 'uint256', - outputCommitment: 'bytes32', - }, -} -const RewardArgs = { - RewardArgs: { - rate: 'uint256', - fee: 'uint256', - instance: 'address', - rewardNullifier: 'bytes32', - extDataHash: 'bytes32', - depositRoot: 'bytes32', - withdrawalRoot: 'bytes32', - extData: RewardExtData.RewardExtData, - account: AccountUpdate.AccountUpdate, - }, -} - -const WithdrawExtData = { - WithdrawExtData: { - fee: 'uint256', - recipient: 'address', - relayer: 'address', - encryptedAccount: 'bytes', - }, -} - -const pedersenHashBuffer = (buffer) => toBN(babyJub.unpackPoint(pedersenHash.hash(buffer))[0].toString()) - -const mimcHash = (items) => toBN(mimcsponge.multiHash(items.map((item) => bigInt(item))).toString()) - -const poseidonHash = (items) => toBN(poseidon(items).toString()) +const poseidonHash = (items) => BigNumber.from(poseidon(items).toString()) const poseidonHash2 = (a, b) => poseidonHash([a, b]) /** Generate random number of specified byte length */ -const randomBN = (nbytes = 31) => new BN(crypto.randomBytes(nbytes)) +const randomBN = (nbytes = 31) => BigNumber.from(crypto.randomBytes(nbytes)) /** BigNumber to hex string of specified length */ const toFixedHex = (number, length = 32) => '0x' + - (number instanceof Buffer ? number.toString('hex') : toBN(number).toString(16)).padStart(length * 2, '0') + (number instanceof Buffer ? number.toString('hex') : BigNumber.from(number).toHexString().slice(2)).padStart(length * 2, '0') -function getExtRewardArgsHash({ relayer, encryptedAccount }) { - const encodedData = web3.eth.abi.encodeParameters( - [RewardExtData], - [{ relayer: toFixedHex(relayer, 20), encryptedAccount }], - ) - const hash = soliditySha3({ t: 'bytes', v: encodedData }) - return '0x00' + hash.slice(4) // cut last byte to make it 31 byte long to fit the snark field -} - -function getExtWithdrawArgsHash({ fee, recipient, relayer, encryptedAccount }) { - const encodedData = web3.eth.abi.encodeParameters( - [WithdrawExtData], - [ - { - fee: toFixedHex(fee, 32), - recipient: toFixedHex(recipient, 20), - relayer: toFixedHex(relayer, 20), - encryptedAccount, - }, - ], - ) - const hash = soliditySha3({ t: 'bytes', v: encodedData }) - return '0x00' + hash.slice(4) // cut first byte to make it 31 byte long to fit the snark field -} - -function packEncryptedMessage(encryptedMessage) { - const nonceBuf = Buffer.from(encryptedMessage.nonce, 'base64') - const ephemPublicKeyBuf = Buffer.from(encryptedMessage.ephemPublicKey, 'base64') - const ciphertextBuf = Buffer.from(encryptedMessage.ciphertext, 'base64') - const messageBuff = Buffer.concat([ - Buffer.alloc(24 - nonceBuf.length), - nonceBuf, - Buffer.alloc(32 - ephemPublicKeyBuf.length), - ephemPublicKeyBuf, - ciphertextBuf, - ]) - return '0x' + messageBuff.toString('hex') -} - -function unpackEncryptedMessage(encryptedMessage) { - if (encryptedMessage.slice(0, 2) === '0x') { - encryptedMessage = encryptedMessage.slice(2) - } - const messageBuff = Buffer.from(encryptedMessage, 'hex') - const nonceBuf = messageBuff.slice(0, 24) - const ephemPublicKeyBuf = messageBuff.slice(24, 56) - const ciphertextBuf = messageBuff.slice(56) - return { - version: 'x25519-xsalsa20-poly1305', - nonce: nonceBuf.toString('base64'), - ephemPublicKey: ephemPublicKeyBuf.toString('base64'), - ciphertext: ciphertextBuf.toString('base64'), - } -} +const toBuffer = (value, length) => Buffer.from(BigNumber.from(value).toHexString().slice(2).padStart(length * 2, '0'), 'hex') function bitsToNumber(bits) { let result = 0 @@ -123,43 +25,11 @@ function bitsToNumber(bits) { return result } -// a = floor(10**18 * e^(-0.0000000001 * amount)) -// yield = BalBefore - (BalBefore * a)/10**18 -function tornadoFormula({ balance, amount, poolWeight = 1e10 }) { - const decimals = new Decimal(10 ** 18) - balance = new Decimal(balance.toString()) - amount = new Decimal(amount.toString()) - poolWeight = new Decimal(poolWeight.toString()) - - const power = amount.div(poolWeight).negated() - const exponent = Decimal.exp(power).mul(decimals) - const newBalance = balance.mul(exponent).div(decimals) - return toBN(balance.sub(newBalance).toFixed(0)) -} - -function reverseTornadoFormula({ balance, tokens, poolWeight = 1e10 }) { - balance = new Decimal(balance.toString()) - tokens = new Decimal(tokens.toString()) - poolWeight = new Decimal(poolWeight.toString()) - - return toBN(poolWeight.times(Decimal.ln(balance.div(balance.sub(tokens)))).toFixed(0)) -} - module.exports = { randomBN, - pedersenHashBuffer, bitsToNumber, - getExtRewardArgsHash, - getExtWithdrawArgsHash, - packEncryptedMessage, - unpackEncryptedMessage, toFixedHex, - mimcHash, + toBuffer, poseidonHash, poseidonHash2, - tornadoFormula, - reverseTornadoFormula, - RewardArgs, - RewardExtData, - AccountUpdate, } diff --git a/test/pack.test.js b/test/pack.test.js index 688dcd4..d9e4643 100644 --- a/test/pack.test.js +++ b/test/pack.test.js @@ -1,39 +1,32 @@ /* global artifacts, web3, contract */ -require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should() -const { takeSnapshot, revertSnapshot } = require('../scripts/ganacheHelper') -const Ccntroller = require('../src/controller') -const { toBN } = require('web3-utils') -const Pack = artifacts.require('Pack') -const jsSHA = require('jssha') - -const { poseidonHash2 } = require('../src/utils') +const { expect } = require("chai") const MerkleTree = require('fixed-merkle-tree') +const jsSHA = require('jssha') +const { poseidonHash2 } = require('../src/utils') +const { batchTreeUpdate, prove } = require('../src/controller') + +const instances = [ + '0xc6325fa78E0764993Bf2997116A3771bCbcb3fa9', + '0xb70738422D0f9d1225300eE0Fc67e7392095567d', + '0xA675B536203a123B0214cdf1EBb1298F440dA19A', + '0xFA1835cf197C3281Dc993a63bb160026dAC98bF3', +] + +const hashes = [ + '0x6f44cd7458bf24f65851fa8097712e3a8d9a6f3e387c501b285338308a74b8f3', + '0xafd3103939b7b0cd7a0ad1ddac57dd13af7f2825a21b47ae995b5bb0f767a106', + '0x57f7b90a3cb4ea6860e6dd5fa44ac4f53ebe6ae3948af577a01ef51738313246' +] const levels = 20 const CHUNK_TREE_HEIGHT = 7 -contract.skip('Pack', (accounts) => { - let pack - let snapshotId - - const instances = [ - '0xc6325fa78E0764993Bf2997116A3771bCbcb3fa9', - '0xb70738422D0f9d1225300eE0Fc67e7392095567d', - '0xA675B536203a123B0214cdf1EBb1298F440dA19A', - '0xFA1835cf197C3281Dc993a63bb160026dAC98bF3', - ] - - const hashes = [ - '0x6f44cd7458bf24f65851fa8097712e3a8d9a6f3e387c501b285338308a74b8f3', - '0xafd3103939b7b0cd7a0ad1ddac57dd13af7f2825a21b47ae995b5bb0f767a106', - '0x57f7b90a3cb4ea6860e6dd5fa44ac4f53ebe6ae3948af577a01ef51738313246' - ] - - const notes = [] - - before(async () => { - const emptyTree = new MerkleTree(levels, [], { hashFunction: poseidonHash2 }) - pack = await Pack.new() +describe.skip('Pack', () => { + it('should work', async () => { + const tree = new MerkleTree(levels, [], { hashFunction: poseidonHash2 }) + const Pack = await ethers.getContractFactory("Pack") + const pack = await Pack.deploy() + const notes = [] for (let i = 0; i < 2 ** CHUNK_TREE_HEIGHT; i++) { notes[i] = { instance: instances[i % instances.length], @@ -41,37 +34,14 @@ contract.skip('Pack', (accounts) => { block: 1 + i, } } + const receipt = await pack.pack2(notes.map(a => a.hash), notes.map(a => a.instance), notes.map(a => a.block)) + const receipt2 = await receipt.wait() - snapshotId = await takeSnapshot() - }) - - describe('#pack', () => { - it('gastest', async () => { - const emptyTree = new MerkleTree(levels, [], { hashFunction: poseidonHash2 }) - const receipt = await pack.pack2(notes.map(a => a.hash), notes.map(a => a.instance), notes.map(a => a.block), { gas: 6e6 }) - console.log('total', receipt.receipt.gasUsed) - - const sha = new jsSHA('SHA-256', 'ARRAYBUFFER') - for (let i = 0; i < notes.length; i++) { - sha.update(toBN(notes[i].hash).toBuffer('be', 32)) - sha.update(toBN(notes[i].instance).toBuffer('be', 20)) - sha.update(toBN(notes[i].block).toBuffer('be', 4)) - } - const hash = sha.getHash('HEX') - - const solHash = await pack.hash() - solHash.should.be.equal('0x' + hash) - console.log('batch size', notes.length) - console.log('events', (await pack.gas1()).toString()) - console.log('hash', (await pack.gas2()).toString()) - console.log('bytes',(await pack.gas3()).toString()) - console.log('calldata', toBN(6e6).sub(await pack.gas4()).toString()) - }) - }) - - afterEach(async () => { - await revertSnapshot(snapshotId.result) - // eslint-disable-next-line require-atomic-updates - snapshotId = await takeSnapshot() + console.log(`total ${receipt2.gasUsed}`) + console.log(`batch size ${notes.length}`) + console.log(`events ${await pack.gas1()}`) + console.log(`hash ${await pack.gas2()}`) + console.log(`bytes ${await pack.gas3()}`) + console.log(`calldata ${receipt.gasLimit.sub(await pack.gas4())}`) }) }) diff --git a/test/sample-test.js b/test/sample-test.js deleted file mode 100644 index 83bee5a..0000000 --- a/test/sample-test.js +++ /dev/null @@ -1,14 +0,0 @@ -const { expect } = require("chai"); - -describe("Greeter", function() { - it("Should return the new greeting once it's changed", async function() { - const Greeter = await ethers.getContractFactory("Greeter"); - const greeter = await Greeter.deploy("Hello, world!"); - - await greeter.deployed(); - expect(await greeter.greet()).to.equal("Hello, world!"); - - await greeter.setGreeting("Hola, mundo!"); - expect(await greeter.greet()).to.equal("Hola, mundo!"); - }); -}); diff --git a/test/snark.test.js b/test/snark.test.js index 6f4d6a9..1217703 100644 --- a/test/snark.test.js +++ b/test/snark.test.js @@ -1,12 +1,12 @@ /* global artifacts, web3, contract */ -require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should() +const { expect } = require("chai") const MerkleTree = require('fixed-merkle-tree') const { poseidonHash2, randomBN } = require('../src/utils') const { batchTreeUpdate, prove } = require('../src/controller') const levels = 20 const CHUNK_TREE_HEIGHT = 2 -contract('Snark', () => { +describe('Snark', () => { it('should work', async () => { const tree = new MerkleTree(levels, [], { hashFunction: poseidonHash2 }) const events = [] @@ -18,6 +18,6 @@ contract('Snark', () => { }) } const data = await batchTreeUpdate(tree, events) - const proof = await prove(data, './build/circuits/BatchTreeUpdate') + const proof = await prove(data, './artifacts/circuits/BatchTreeUpdate') }) }) diff --git a/test/tornadoTrees.test.js b/test/tornadoTrees.test.js index deeef7a..9720487 100644 --- a/test/tornadoTrees.test.js +++ b/test/tornadoTrees.test.js @@ -1,95 +1,59 @@ -/* global artifacts, web3, contract */ -require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should() -const { takeSnapshot, revertSnapshot } = require('../scripts/ganacheHelper') -const controller = require('../src/controller') -const TornadoTrees = artifacts.require('TornadoTreesMock') -const BatchTreeUpdateVerifier = artifacts.require('BatchTreeUpdateVerifier') -const { toBN } = require('web3-utils') - -const { toBN } = require('web3-utils') +const { expect } = require("chai") const { toFixedHex, poseidonHash2, randomBN } = require('../src/utils') const MerkleTree = require('fixed-merkle-tree') - -async function registerDeposit(note, tornadoTrees, from) { - await tornadoTrees.setBlockNumber(note.depositBlock) - await tornadoTrees.registerDeposit(note.instance, toFixedHex(note.commitment), { from }) - return { - instance: note.instance, - hash: toFixedHex(note.commitment), - block: toFixedHex(note.depositBlock), - } -} - -async function registerWithdrawal(note, tornadoTrees, from) { - await tornadoTrees.setBlockNumber(note.withdrawalBlock) - await tornadoTrees.registerWithdrawal(note.instance, toFixedHex(note.nullifierHash), { from }) - return { - instance: note.instance, - hash: toFixedHex(note.nullifierHash), - block: toFixedHex(note.withdrawalBlock), - } -} +const controller = require('../src/controller') async function register(note, tornadoTrees, from) { - await tornadoTrees.register( + await tornadoTrees.connect(from).register( note.instance, toFixedHex(note.commitment), toFixedHex(note.nullifierHash), note.depositBlock, note.withdrawalBlock, - { - from, - }, ) - return { - instance: note.instance, - hash: toFixedHex(note.nullifierHash), - block: toFixedHex(note.withdrawalBlock), - } } +const toEns = (addr) => toFixedHex(addr, 20).padEnd(66, '0') + const levels = 20 const CHUNK_TREE_HEIGHT = 2 -contract('TornadoTrees', (accounts) => { - let tornadoTrees + +const instances = [ + '0x1111000000000000000000000000000000001111', + '0x2222000000000000000000000000000000002222', + '0x3333000000000000000000000000000000003333', + '0x4444000000000000000000000000000000004444', +] + +const blocks = ['0xaaaaaaaa', '0xbbbbbbbb', '0xcccccccc', '0xdddddddd'] + +describe("TornadoTrees", function() { + let tree + let operator + let tornadoProxy let verifier - // let controller - let snapshotId - let tornadoProxy = accounts[0] - let operator = accounts[0] + let tornadoTrees + let notes + let events - const instances = [ - '0x1111000000000000000000000000000000001111', - '0x2222000000000000000000000000000000002222', - '0x3333000000000000000000000000000000003333', - '0x4444000000000000000000000000000000004444', - ] + beforeEach(async function() { + tree = new MerkleTree(levels, [], { hashFunction: poseidonHash2 }) + ;[operator, tornadoProxy] = await ethers.getSigners() - const blocks = ['0xaaaaaaaa', '0xbbbbbbbb', '0xcccccccc', '0xdddddddd'] + const BatchTreeUpdateVerifier = await ethers.getContractFactory("BatchTreeUpdateVerifier") + verifier = await BatchTreeUpdateVerifier.deploy() - const notes = [] - - before(async () => { - const emptyTree = new MerkleTree(levels, [], { hashFunction: poseidonHash2 }) - verifier = await BatchTreeUpdateVerifier.new() - tornadoTrees = await TornadoTrees.new( - operator, - tornadoProxy, - verifier.address, - toFixedHex(emptyTree.root()), - toFixedHex(emptyTree.root()), + const TornadoTrees = await ethers.getContractFactory("TornadoTreesMock") + tornadoTrees = await TornadoTrees.deploy( + toEns(operator.address), + toEns(tornadoProxy.address), + toEns(verifier.address), + toFixedHex(tree.root()), + toFixedHex(tree.root()), ) - // controller = new Controller({ - // contract: '', - // tornadoTreesContract: tornadoTrees, - // merkleTreeHeight: levels, - // provingKeys, - // }) - // await controller.init() - + notes = [] for (let i = 0; i < 2 ** CHUNK_TREE_HEIGHT; i++) { - // onsole.log('i', i) notes[i] = { instance: instances[i % instances.length], depositBlock: blocks[i % blocks.length], @@ -100,60 +64,42 @@ contract('TornadoTrees', (accounts) => { await register(notes[i], tornadoTrees, tornadoProxy) } - snapshotId = await takeSnapshot() + events = notes.map((note) => ({ + hash: toFixedHex(note.commitment), + instance: toFixedHex(note.instance, 20), + block: toFixedHex(note.depositBlock, 4), + })) }) - describe('#updateDepositTree', () => { - it('should check hash', async () => { - const emptyTree = new MerkleTree(levels, [], { hashFunction: poseidonHash2 }) - const events = notes.map((note) => ({ - hash: toFixedHex(note.commitment), - instance: toFixedHex(note.instance, 20), - block: toFixedHex(note.depositBlock, 4), - })) - const data = await controller.batchTreeUpdate(emptyTree, events) - const solHash = await tornadoTrees.updateDepositTreeMock( - toFixedHex(data.oldRoot), - toFixedHex(data.newRoot), - toFixedHex(data.pathIndices, 4), - events, - ) - toBN(data.argsHash).should.be.eq.BN(solHash) - }) - - it('should prove snark', async () => { - const emptyTree = new MerkleTree(levels, [], { hashFunction: poseidonHash2 }) - const events = notes.map((note) => ({ - hash: toFixedHex(note.commitment), - instance: toFixedHex(note.instance, 20), - block: toFixedHex(note.depositBlock, 4), - })) - const data = await controller.batchTreeUpdate(emptyTree, events) - const proof = await controller.prove(data, './build/circuits/BatchTreeUpdate') - await tornadoTrees.updateDepositTree( - proof, - toFixedHex(data.argsHash), - toFixedHex(data.oldRoot), - toFixedHex(data.newRoot), - toFixedHex(data.pathIndices, 4), - events, - ) - - const updatedRoot = await tornadoTrees.depositRoot() - updatedRoot.should.be.eq.BN(toBN(toFixedHex(data.newRoot))) - }) - - it('should work for non-empty tree') - it('should reject for partially filled tree') - it('should reject for outdated deposit root') - it('should reject for incorrect insert index') - it('should reject for overflows of newRoot') - it('should reject for invalid sha256 args') + it("Should calculate hash", async function() { + const data = await controller.batchTreeUpdate(tree, events) + const solHash = await tornadoTrees.updateDepositTreeMock( + toFixedHex(data.oldRoot), + toFixedHex(data.newRoot), + toFixedHex(data.pathIndices, 4), + events, + ) + expect(solHash).to.be.equal(data.argsHash) }) - afterEach(async () => { - await revertSnapshot(snapshotId.result) - // eslint-disable-next-line require-atomic-updates - snapshotId = await takeSnapshot() + it("Should calculate hash", async function() { + const data = await controller.batchTreeUpdate(tree, events) + const proof = await controller.prove(data, './artifacts/circuits/BatchTreeUpdate') + await tornadoTrees.updateDepositTree( + proof, + toFixedHex(data.argsHash), + toFixedHex(data.oldRoot), + toFixedHex(data.newRoot), + toFixedHex(data.pathIndices, 4), + events, + ) + expect(await tornadoTrees.depositRoot()).to.be.equal(tree.root()) }) + + it('should work for non-empty tree') + it('should reject for partially filled tree') + it('should reject for outdated deposit root') + it('should reject for incorrect insert index') + it('should reject for overflows of newRoot') + it('should reject for invalid sha256 args') })