From 8b77b733232e17fe4392e813567c6d42bdf27f67 Mon Sep 17 00:00:00 2001 From: gozzy Date: Fri, 2 Sep 2022 17:56:58 +0000 Subject: [PATCH] initialise --- LICENSE | 22 + README.md | 33 + circuits/BatchTreeUpdate.circom | 91 + circuits/MerkleTree.circom | 71 + circuits/MerkleTreeUpdater.circom | 33 + circuits/Utils.circom | 67 + contracts/AdminUpgradeableProxy.sol | 19 + contracts/TornadoTrees.sol | 281 + .../interfaces/IBatchTreeUpdateVerifier.sol | 7 + contracts/interfaces/ITornadoTreesV1.sol | 21 + contracts/mocks/Pack.sol | 74 + contracts/mocks/PublicArray.sol | 28 + contracts/mocks/TornadoTreesMock.sol | 100 + contracts/mocks/TornadoTreesV1Mock.sol | 79 + .../verifiers/BatchTreeUpdateVerifier.sol | 1 + hardhat.config.js | 46 + optimize/Dockerfile | 47 + optimize/node.sh | 34 + package.json | 56 + scripts/buildCircuit.sh | 11 + scripts/buildWitness.sh | 8 + scripts/changeTreeHeight.sh | 7 + scripts/deploy.js | 114 + scripts/deployEmptyV1.js | 115 + src/index.js | 123 + src/utils.js | 45 + test/binarySearch.test.js | 147 + test/pack.test.js | 44 + test/snark.test.js | 24 + test/tornadoTrees.test.js | 299 + yarn.lock | 9230 +++++++++++++++++ 31 files changed, 11277 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 circuits/BatchTreeUpdate.circom create mode 100644 circuits/MerkleTree.circom create mode 100644 circuits/MerkleTreeUpdater.circom create mode 100644 circuits/Utils.circom create mode 100644 contracts/AdminUpgradeableProxy.sol create mode 100644 contracts/TornadoTrees.sol create mode 100644 contracts/interfaces/IBatchTreeUpdateVerifier.sol create mode 100644 contracts/interfaces/ITornadoTreesV1.sol create mode 100644 contracts/mocks/Pack.sol create mode 100644 contracts/mocks/PublicArray.sol create mode 100644 contracts/mocks/TornadoTreesMock.sol create mode 100644 contracts/mocks/TornadoTreesV1Mock.sol create mode 120000 contracts/verifiers/BatchTreeUpdateVerifier.sol create mode 100644 hardhat.config.js create mode 100644 optimize/Dockerfile create mode 100755 optimize/node.sh create mode 100644 package.json create mode 100755 scripts/buildCircuit.sh create mode 100755 scripts/buildWitness.sh create mode 100755 scripts/changeTreeHeight.sh create mode 100644 scripts/deploy.js create mode 100644 scripts/deployEmptyV1.js create mode 100644 src/index.js create mode 100644 src/utils.js create mode 100644 test/binarySearch.test.js create mode 100644 test/pack.test.js create mode 100644 test/snark.test.js create mode 100644 test/tornadoTrees.test.js create mode 100644 yarn.lock diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bb98c92 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2018 Truffle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..229dcb0 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# Tornado.cash trees [![Build Status](https://github.com/tornadocash/tornado-trees/workflows/build/badge.svg)](https://github.com/tornadocash/tornado-trees/actions) + +This repo implements a more optimized version of the [TornadoTrees](https://github.com/tornadocash/tornado-anonymity-mining/blob/080d0f83665fa686d7fe42dd57fb5975d0f1ca58/contracts/TornadoTrees.sol) mechanism. + +## Dependencies + +1. node 12 +2. yarn +3. zkutil (`brew install rust && cargo install zkutil`) + +## Start + +```bash +$ yarn +$ yarn circuit +$ yarn test +``` + +## Mainnet testing + +```bash +$ yarn circuit +$ npx hardhat node --fork --fork-block-number 11827889 +$ npx hardhat test +``` + +## build large circuits + +Make sure you have enough RAM + +```bash +docker build . -t tornadocash/tornado-trees +``` diff --git a/circuits/BatchTreeUpdate.circom b/circuits/BatchTreeUpdate.circom new file mode 100644 index 0000000..1896b53 --- /dev/null +++ b/circuits/BatchTreeUpdate.circom @@ -0,0 +1,91 @@ +include "../node_modules/circomlib/circuits/poseidon.circom"; +include "../node_modules/circomlib/circuits/bitify.circom"; +include "./MerkleTreeUpdater.circom"; +include "./Utils.circom"; + +template TreeLayer(height) { + signal input ins[1 << (height + 1)]; + signal output outs[1 << height]; + + component hash[1 << height]; + for(var i = 0; i < (1 << height); i++) { + hash[i] = HashLeftRight(); + hash[i].left <== ins[i * 2]; + hash[i].right <== ins[i * 2 + 1]; + hash[i].hash ==> outs[i]; + } +} + +// Inserts a leaf batch into a tree +// Checks that tree previously contained zero leaves in the same position +template BatchTreeUpdate(levels, batchLevels, zeroBatchLeaf) { + var height = levels - batchLevels; + var nLeaves = 1 << batchLevels; + signal input argsHash; + signal private input oldRoot; + signal private input newRoot; + signal private input pathIndices; + signal private input pathElements[height]; + signal private input hashes[nLeaves]; + signal private input instances[nLeaves]; + signal private input blocks[nLeaves]; + + // Check that hash of arguments is correct + // We compress arguments into a single hash to considerably reduce gas usage on chain + component argsHasher = TreeUpdateArgsHasher(nLeaves); + argsHasher.oldRoot <== oldRoot; + argsHasher.newRoot <== newRoot; + argsHasher.pathIndices <== pathIndices; + for(var i = 0; i < nLeaves; i++) { + argsHasher.hashes[i] <== hashes[i]; + argsHasher.instances[i] <== instances[i]; + argsHasher.blocks[i] <== blocks[i]; + } + argsHash === argsHasher.out; + + // Compute hashes of all leaves + component leaves[nLeaves]; + for(var i = 0; i < nLeaves; i++) { + leaves[i] = Poseidon(3); + leaves[i].inputs[0] <== instances[i]; + leaves[i].inputs[1] <== hashes[i]; + leaves[i].inputs[2] <== blocks[i]; + } + + // Compute batch subtree merkle root + component layers[batchLevels]; + for(var level = batchLevels - 1; level >= 0; level--) { + layers[level] = TreeLayer(level); + for(var i = 0; i < (1 << (level + 1)); i++) { + layers[level].ins[i] <== level == batchLevels - 1 ? leaves[i].out : layers[level + 1].outs[i]; + } + } + + // Verify that batch subtree was inserted correctly + component treeUpdater = MerkleTreeUpdater(height, zeroBatchLeaf); + treeUpdater.oldRoot <== oldRoot; + treeUpdater.newRoot <== newRoot; + treeUpdater.leaf <== layers[0].outs[0]; + treeUpdater.pathIndices <== pathIndices; + for(var i = 0; i < height; i++) { + treeUpdater.pathElements[i] <== pathElements[i]; + } +} + +// zeroLeaf = keccak256("tornado") % FIELD_SIZE +// zeroBatchLeaf is poseidon(zeroLeaf, zeroLeaf) (batchLevels - 1) times +function nthZero(n) { + if (n == 0) return 21663839004416932945382355908790599225266501822907911457504978515578255421292; + if (n == 1) return 11850551329423159860688778991827824730037759162201783566284850822760196767874; + if (n == 2) return 21572503925325825116380792768937986743990254033176521064707045559165336555197; + if (n == 3) return 11224495635916644180335675565949106569141882748352237685396337327907709534945; + if (n == 4) return 2399242030534463392142674970266584742013168677609861039634639961298697064915; + if (n == 5) return 13182067204896548373877843501261957052850428877096289097123906067079378150834; + if (n == 6) return 7106632500398372645836762576259242192202230138343760620842346283595225511823; + if (n == 7) return 17857585024203959071818533000506593455576509792639288560876436361491747801924; + if (n == 8) return 17278668323652664881420209773995988768195998574629614593395162463145689805534; + if (n == 9) return 209436188287252095316293336871467217491997565239632454977424802439169726471; +} + +var CHUNK_TREE_HEIGHT = 8 +component main = BatchTreeUpdate(20, CHUNK_TREE_HEIGHT, nthZero(CHUNK_TREE_HEIGHT)) diff --git a/circuits/MerkleTree.circom b/circuits/MerkleTree.circom new file mode 100644 index 0000000..c508e18 --- /dev/null +++ b/circuits/MerkleTree.circom @@ -0,0 +1,71 @@ +include "../node_modules/circomlib/circuits/poseidon.circom"; +include "../node_modules/circomlib/circuits/bitify.circom"; + +// Computes Poseidon([left, right]) +template HashLeftRight() { + signal input left; + signal input right; + signal output hash; + + component hasher = Poseidon(2); + hasher.inputs[0] <== left; + hasher.inputs[1] <== right; + hash <== hasher.out; +} + +// if s == 0 returns [in[0], in[1]] +// if s == 1 returns [in[1], in[0]] +template DualMux() { + signal input in[2]; + signal input s; + signal output out[2]; + + s * (1 - s) === 0; + out[0] <== (in[1] - in[0])*s + in[0]; + out[1] <== (in[0] - in[1])*s + in[1]; +} + +// Verifies that merkle proof is correct for given merkle root and a leaf +// pathIndices input is an array of 0/1 selectors telling whether given pathElement is on the left or right side of merkle path +template RawMerkleTree(levels) { + signal input leaf; + signal input pathElements[levels]; + signal input pathIndices[levels]; + + signal output root; + + component selectors[levels]; + component hashers[levels]; + + for (var i = 0; i < levels; i++) { + selectors[i] = DualMux(); + selectors[i].in[0] <== i == 0 ? leaf : hashers[i - 1].hash; + selectors[i].in[1] <== pathElements[i]; + selectors[i].s <== pathIndices[i]; + + hashers[i] = HashLeftRight(); + hashers[i].left <== selectors[i].out[0]; + hashers[i].right <== selectors[i].out[1]; + } + + root <== hashers[levels - 1].hash; +} + +template MerkleTree(levels) { + signal input leaf; + signal input pathElements[levels]; + signal input pathIndices; + signal output root; + + component indexBits = Num2Bits(levels); + indexBits.in <== pathIndices; + + component tree = RawMerkleTree(levels) + tree.leaf <== leaf; + for (var i = 0; i < levels; i++) { + tree.pathIndices[i] <== indexBits.out[i]; + tree.pathElements[i] <== pathElements[i]; + } + + root <== tree.root +} diff --git a/circuits/MerkleTreeUpdater.circom b/circuits/MerkleTreeUpdater.circom new file mode 100644 index 0000000..eb8a83b --- /dev/null +++ b/circuits/MerkleTreeUpdater.circom @@ -0,0 +1,33 @@ +include "./MerkleTree.circom"; + +// inserts a leaf into a tree +// checks that tree previously contained zero in the same position +template MerkleTreeUpdater(levels, zeroLeaf) { + signal input oldRoot; + signal input newRoot; + signal input leaf; + signal input pathIndices; + signal private input pathElements[levels]; + + // Compute indexBits once for both trees + // Since Num2Bits is non deterministic, 2 duplicate calls to it cannot be + // optimized by circom compiler + component indexBits = Num2Bits(levels); + indexBits.in <== pathIndices; + + component treeBefore = RawMerkleTree(levels); + for(var i = 0; i < levels; i++) { + treeBefore.pathIndices[i] <== indexBits.out[i]; + treeBefore.pathElements[i] <== pathElements[i]; + } + treeBefore.leaf <== zeroLeaf; + treeBefore.root === oldRoot; + + component treeAfter = RawMerkleTree(levels); + for(var i = 0; i < levels; i++) { + treeAfter.pathIndices[i] <== indexBits.out[i]; + treeAfter.pathElements[i] <== pathElements[i]; + } + treeAfter.leaf <== leaf; + treeAfter.root === newRoot; +} diff --git a/circuits/Utils.circom b/circuits/Utils.circom new file mode 100644 index 0000000..4ac6e4f --- /dev/null +++ b/circuits/Utils.circom @@ -0,0 +1,67 @@ +include "../node_modules/circomlib/circuits/bitify.circom"; +include "../node_modules/circomlib/circuits/sha256/sha256.circom"; + +template TreeUpdateArgsHasher(nLeaves) { + signal private input oldRoot; + signal private input newRoot; + signal private input pathIndices; + signal private input instances[nLeaves]; + signal private input hashes[nLeaves]; + signal private input blocks[nLeaves]; + signal output out; + + var header = 256 + 256 + 32; + var bitsPerLeaf = 160 + 256 + 32; + component hasher = Sha256(header + nLeaves * bitsPerLeaf); + + // the range check on old root is optional, it's enforced by smart contract anyway + component bitsOldRoot = Num2Bits_strict(); + component bitsNewRoot = Num2Bits_strict(); + component bitsPathIndices = Num2Bits(32); + component bitsInstance[nLeaves]; + component bitsHash[nLeaves]; + component bitsBlock[nLeaves]; + + bitsOldRoot.in <== oldRoot; + bitsNewRoot.in <== newRoot; + bitsPathIndices.in <== pathIndices; + + hasher.in[0] <== 0; + hasher.in[1] <== 0; + for(var i = 0; i < 254; i++) { + hasher.in[i + 2] <== bitsOldRoot.out[253 - i]; + } + hasher.in[256] <== 0; + hasher.in[257] <== 0; + for(var i = 0; i < 254; i++) { + hasher.in[i + 258] <== bitsNewRoot.out[253 - i]; + } + for(var i = 0; i < 32; i++) { + hasher.in[i + 512] <== bitsPathIndices.out[31 - i]; + } + for(var leaf = 0; leaf < nLeaves; leaf++) { + // the range check on hash is optional, it's enforced by the smart contract anyway + bitsHash[leaf] = Num2Bits_strict(); + bitsInstance[leaf] = Num2Bits(160); + bitsBlock[leaf] = Num2Bits(32); + bitsHash[leaf].in <== hashes[leaf]; + bitsInstance[leaf].in <== instances[leaf]; + bitsBlock[leaf].in <== blocks[leaf]; + hasher.in[header + leaf * bitsPerLeaf + 0] <== 0; + hasher.in[header + leaf * bitsPerLeaf + 1] <== 0; + for(var i = 0; i < 254; i++) { + hasher.in[header + leaf * bitsPerLeaf + i + 2] <== bitsHash[leaf].out[253 - i]; + } + for(var i = 0; i < 160; i++) { + hasher.in[header + leaf * bitsPerLeaf + i + 256] <== bitsInstance[leaf].out[159 - i]; + } + for(var i = 0; i < 32; i++) { + hasher.in[header + leaf * bitsPerLeaf + i + 416] <== bitsBlock[leaf].out[31 - i]; + } + } + component b2n = Bits2Num(256); + for (var i = 0; i < 256; i++) { + b2n.in[i] <== hasher.out[255 - i]; + } + out <== b2n.out; +} diff --git a/contracts/AdminUpgradeableProxy.sol b/contracts/AdminUpgradeableProxy.sol new file mode 100644 index 0000000..ca1649d --- /dev/null +++ b/contracts/AdminUpgradeableProxy.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "@openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol"; + +/** + * @dev TransparentUpgradeableProxy where admin is allowed to call implementation methods. + */ +contract AdminUpgradeableProxy is TransparentUpgradeableProxy { + /** + * @dev Initializes an upgradeable proxy backed by the implementation at `_logic`. + */ + constructor(address _logic, bytes memory _data) public payable TransparentUpgradeableProxy(_logic, msg.sender, _data) {} + + /** + * @dev Override to allow admin access the fallback function. + */ + function _beforeFallback() internal override {} +} diff --git a/contracts/TornadoTrees.sol b/contracts/TornadoTrees.sol new file mode 100644 index 0000000..65b2c8e --- /dev/null +++ b/contracts/TornadoTrees.sol @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + +import "./interfaces/ITornadoTreesV1.sol"; +import "./interfaces/IBatchTreeUpdateVerifier.sol"; +import "@openzeppelin/upgrades-core/contracts/Initializable.sol"; + +/// @dev This contract holds a merkle tree of all tornado cash deposit and withdrawal events +contract TornadoTrees is Initializable { + address public immutable governance; + bytes32 public depositRoot; + bytes32 public previousDepositRoot; + bytes32 public withdrawalRoot; + bytes32 public previousWithdrawalRoot; + address public tornadoProxy; + IBatchTreeUpdateVerifier public treeUpdateVerifier; + ITornadoTreesV1 public immutable tornadoTreesV1; + + uint256 public constant CHUNK_TREE_HEIGHT = 8; + 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; + uint256 public constant SNARK_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + + mapping(uint256 => bytes32) public deposits; + uint256 public depositsLength; + uint256 public lastProcessedDepositLeaf; + uint256 public immutable depositsV1Length; + + mapping(uint256 => bytes32) public withdrawals; + uint256 public withdrawalsLength; + uint256 public lastProcessedWithdrawalLeaf; + uint256 public immutable withdrawalsV1Length; + + event DepositData(address instance, bytes32 indexed hash, uint256 block, uint256 index); + event WithdrawalData(address instance, bytes32 indexed hash, uint256 block, uint256 index); + + struct TreeLeaf { + bytes32 hash; + address instance; + uint32 block; + } + + modifier onlyTornadoProxy { + require(msg.sender == tornadoProxy, "Not authorized"); + _; + } + + modifier onlyGovernance() { + require(msg.sender == governance, "Only governance can perform this action"); + _; + } + + struct SearchParams { + uint256 depositsFrom; + uint256 depositsStep; + uint256 withdrawalsFrom; + uint256 withdrawalsStep; + } + + constructor( + address _governance, + ITornadoTreesV1 _tornadoTreesV1, + SearchParams memory _searchParams + ) public { + governance = _governance; + tornadoTreesV1 = _tornadoTreesV1; + + depositsV1Length = findArrayLength( + _tornadoTreesV1, + "deposits(uint256)", + _searchParams.depositsFrom, + _searchParams.depositsStep + ); + + withdrawalsV1Length = findArrayLength( + _tornadoTreesV1, + "withdrawals(uint256)", + _searchParams.withdrawalsFrom, + _searchParams.withdrawalsStep + ); + } + + function initialize(address _tornadoProxy, IBatchTreeUpdateVerifier _treeUpdateVerifier) public initializer onlyGovernance { + tornadoProxy = _tornadoProxy; + treeUpdateVerifier = _treeUpdateVerifier; + + depositRoot = tornadoTreesV1.depositRoot(); + uint256 lastDepositLeaf = tornadoTreesV1.lastProcessedDepositLeaf(); + require(lastDepositLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state"); + lastProcessedDepositLeaf = lastDepositLeaf; + depositsLength = depositsV1Length; + + withdrawalRoot = tornadoTreesV1.withdrawalRoot(); + uint256 lastWithdrawalLeaf = tornadoTreesV1.lastProcessedWithdrawalLeaf(); + require(lastWithdrawalLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state"); + lastProcessedWithdrawalLeaf = lastWithdrawalLeaf; + withdrawalsLength = withdrawalsV1Length; + } + + function registerDeposit(address _instance, bytes32 _commitment) public onlyTornadoProxy { + uint256 _depositsLength = depositsLength; + deposits[_depositsLength] = keccak256(abi.encode(_instance, _commitment, blockNumber())); + emit DepositData(_instance, _commitment, blockNumber(), _depositsLength); + depositsLength = _depositsLength + 1; + } + + function registerWithdrawal(address _instance, bytes32 _nullifierHash) public onlyTornadoProxy { + uint256 _withdrawalsLength = withdrawalsLength; + withdrawals[_withdrawalsLength] = keccak256(abi.encode(_instance, _nullifierHash, blockNumber())); + emit WithdrawalData(_instance, _nullifierHash, blockNumber(), _withdrawalsLength); + withdrawalsLength = _withdrawalsLength + 1; + } + + function updateDepositTree( + bytes calldata _proof, + bytes32 _argsHash, + bytes32 _currentRoot, + bytes32 _newRoot, + uint32 _pathIndices, + TreeLeaf[CHUNK_SIZE] calldata _events + ) public { + uint256 offset = lastProcessedDepositLeaf; + require(_newRoot != previousDepositRoot, "Outdated deposit root"); + require(_currentRoot == depositRoot, "Proposed deposit root is invalid"); + require(_pathIndices == offset >> CHUNK_TREE_HEIGHT, "Incorrect deposit insert index"); + + bytes memory data = new bytes(BYTES_SIZE); + assembly { + mstore(add(data, 0x44), _pathIndices) + mstore(add(data, 0x40), _newRoot) + mstore(add(data, 0x20), _currentRoot) + } + for (uint256 i = 0; i < CHUNK_SIZE; i++) { + (bytes32 hash, address instance, uint32 blockNumber) = (_events[i].hash, _events[i].instance, _events[i].block); + bytes32 leafHash = keccak256(abi.encode(instance, hash, blockNumber)); + bytes32 deposit = offset + i >= depositsV1Length ? deposits[offset + i] : tornadoTreesV1.deposits(offset + i); + require(leafHash == deposit, "Incorrect deposit"); + assembly { + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x7c), blockNumber) + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x78), instance) + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x64), hash) + } + if (offset + i >= depositsV1Length) { + delete deposits[offset + i]; + } else { + emit DepositData(instance, hash, blockNumber, offset + i); + } + } + + uint256 argsHash = uint256(sha256(data)) % SNARK_FIELD; + require(argsHash == uint256(_argsHash), "Invalid args hash"); + require(treeUpdateVerifier.verifyProof(_proof, [argsHash]), "Invalid deposit tree update proof"); + + previousDepositRoot = _currentRoot; + depositRoot = _newRoot; + lastProcessedDepositLeaf = offset + CHUNK_SIZE; + } + + function updateWithdrawalTree( + bytes calldata _proof, + bytes32 _argsHash, + bytes32 _currentRoot, + bytes32 _newRoot, + uint32 _pathIndices, + TreeLeaf[CHUNK_SIZE] calldata _events + ) public { + uint256 offset = lastProcessedWithdrawalLeaf; + require(_newRoot != previousWithdrawalRoot, "Outdated withdrawal root"); + require(_currentRoot == withdrawalRoot, "Proposed withdrawal root is invalid"); + require(_pathIndices == offset >> CHUNK_TREE_HEIGHT, "Incorrect withdrawal insert index"); + + bytes memory data = new bytes(BYTES_SIZE); + assembly { + mstore(add(data, 0x44), _pathIndices) + mstore(add(data, 0x40), _newRoot) + mstore(add(data, 0x20), _currentRoot) + } + for (uint256 i = 0; i < CHUNK_SIZE; i++) { + (bytes32 hash, address instance, uint32 blockNumber) = (_events[i].hash, _events[i].instance, _events[i].block); + bytes32 leafHash = keccak256(abi.encode(instance, hash, blockNumber)); + bytes32 withdrawal = offset + i >= withdrawalsV1Length ? withdrawals[offset + i] : tornadoTreesV1.withdrawals(offset + i); + require(leafHash == withdrawal, "Incorrect withdrawal"); + assembly { + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x7c), blockNumber) + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x78), instance) + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x64), hash) + } + if (offset + i >= withdrawalsV1Length) { + delete withdrawals[offset + i]; + } else { + emit WithdrawalData(instance, hash, blockNumber, offset + i); + } + } + + uint256 argsHash = uint256(sha256(data)) % SNARK_FIELD; + require(argsHash == uint256(_argsHash), "Invalid args hash"); + require(treeUpdateVerifier.verifyProof(_proof, [argsHash]), "Invalid withdrawal tree update proof"); + + previousWithdrawalRoot = _currentRoot; + withdrawalRoot = _newRoot; + lastProcessedWithdrawalLeaf = offset + CHUNK_SIZE; + } + + function validateRoots(bytes32 _depositRoot, bytes32 _withdrawalRoot) public view { + require(_depositRoot == depositRoot || _depositRoot == previousDepositRoot, "Incorrect deposit tree root"); + require(_withdrawalRoot == withdrawalRoot || _withdrawalRoot == previousWithdrawalRoot, "Incorrect withdrawal tree root"); + } + + /// @dev There is no array length getter for deposit and withdrawal arrays + /// in the previous contract, so we have to find them length manually. + /// Used only during deployment + function findArrayLength( + ITornadoTreesV1 _tornadoTreesV1, + string memory _type, + uint256 _from, // most likely array length after the proposal has passed + uint256 _step // optimal step size to find first match, approximately equals dispersion + ) internal view returns (uint256) { + if (_from == 0 && _step == 0) { + return 0; // for tests + } + // Find the segment with correct array length + bool direction = elementExists(_tornadoTreesV1, _type, _from); + do { + _from = direction ? _from + _step : _from - _step; + } while (direction == elementExists(_tornadoTreesV1, _type, _from)); + uint256 high = direction ? _from : _from + _step; + uint256 low = direction ? _from - _step : _from; + uint256 mid = (high + low) / 2; + + // Perform a binary search in this segment + while (low < mid) { + if (elementExists(_tornadoTreesV1, _type, mid)) { + low = mid; + } else { + high = mid; + } + mid = (low + high) / 2; + } + return mid + 1; + } + + function elementExists( + ITornadoTreesV1 _tornadoTreesV1, + string memory _type, + uint256 index + ) public view returns (bool success) { + // Try to get the element. If it succeeds the array length is higher, it it reverts the length is equal or lower + (success, ) = address(_tornadoTreesV1).staticcall{ gas: 2500 }(abi.encodeWithSignature(_type, index)); + } + + function getRegisteredDeposits() external view returns (bytes32[] memory _deposits) { + uint256 count = depositsLength - lastProcessedDepositLeaf; + _deposits = new bytes32[](count); + for (uint256 i = 0; i < count; i++) { + _deposits[i] = deposits[lastProcessedDepositLeaf + i]; + } + } + + function getRegisteredWithdrawals() external view returns (bytes32[] memory _withdrawals) { + uint256 count = withdrawalsLength - lastProcessedWithdrawalLeaf; + _withdrawals = new bytes32[](count); + for (uint256 i = 0; i < count; i++) { + _withdrawals[i] = withdrawals[lastProcessedWithdrawalLeaf + i]; + } + } + + function setTornadoProxyContract(address _tornadoProxy) external onlyGovernance { + tornadoProxy = _tornadoProxy; + } + + function setVerifierContract(IBatchTreeUpdateVerifier _treeUpdateVerifier) external onlyGovernance { + treeUpdateVerifier = _treeUpdateVerifier; + } + + function blockNumber() public view virtual returns (uint256) { + return block.number; + } +} diff --git a/contracts/interfaces/IBatchTreeUpdateVerifier.sol b/contracts/interfaces/IBatchTreeUpdateVerifier.sol new file mode 100644 index 0000000..c16aaa4 --- /dev/null +++ b/contracts/interfaces/IBatchTreeUpdateVerifier.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +interface IBatchTreeUpdateVerifier { + function verifyProof(bytes calldata proof, uint256[1] calldata input) external view returns (bool); +} diff --git a/contracts/interfaces/ITornadoTreesV1.sol b/contracts/interfaces/ITornadoTreesV1.sol new file mode 100644 index 0000000..6670f37 --- /dev/null +++ b/contracts/interfaces/ITornadoTreesV1.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +interface ITornadoTreesV1 { + function lastProcessedDepositLeaf() external view returns (uint256); + + function lastProcessedWithdrawalLeaf() external view returns (uint256); + + function depositRoot() external view returns (bytes32); + + function withdrawalRoot() external view returns (bytes32); + + function deposits(uint256 i) external view returns (bytes32); + + function withdrawals(uint256 i) external view returns (bytes32); + + function registerDeposit(address instance, bytes32 commitment) external; + + function registerWithdrawal(address instance, bytes32 nullifier) external; +} diff --git a/contracts/mocks/Pack.sol b/contracts/mocks/Pack.sol new file mode 100644 index 0000000..1c3bce0 --- /dev/null +++ b/contracts/mocks/Pack.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +contract Pack { + uint256 public constant CHUNK_TREE_HEIGHT = 8; + uint256 public constant CHUNK_SIZE = 2**CHUNK_TREE_HEIGHT; + uint256 public constant ITEM_SIZE = 32 + 20 + 4; + uint256 public constant BYTES_SIZE = CHUNK_SIZE * ITEM_SIZE; + + uint256 public gas1; + uint256 public gas2; + uint256 public gas3; + uint256 public gas4; + bytes32 public hash; + + event DepositData(address instance, bytes32 indexed hash, uint256 block, uint256 index); + + function pack2( + bytes32[CHUNK_SIZE] memory hashes, + address[CHUNK_SIZE] memory instances, + uint32[CHUNK_SIZE] memory blocks + ) public { + uint256 gasBefore = gasleft(); + bytes memory data = new bytes(BYTES_SIZE); + for (uint256 i = 0; i < CHUNK_SIZE; i++) { + (bytes32 _hash, address _instance, uint32 _block) = (hashes[i], instances[i], blocks[i]); + assembly { + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x38), _block) + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x34), _instance) + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x20), _hash) + } + } + uint256 gasHash = gasleft(); + bytes32 hash1 = sha256(data); + uint256 gasEvents = gasleft(); + for (uint256 i = 0; i < CHUNK_SIZE; i++) { + emit DepositData(instances[i], hashes[i], blocks[i], i); + } + gas1 = gasEvents - gasleft(); + gas2 = gasHash - gasEvents; + gas3 = gasBefore - gasHash; + gas4 = gasBefore; + hash = hash1; + } + + function pack3( + bytes32[CHUNK_SIZE] memory hashes, + address[CHUNK_SIZE] memory instances, + uint32[CHUNK_SIZE] memory blocks + ) + public + view + returns ( + uint256, + uint256, + bytes32 + ) + { + uint256 gasBefore = gasleft(); + bytes memory data = new bytes(BYTES_SIZE); + for (uint256 i = 0; i < CHUNK_SIZE; i++) { + (bytes32 _hash, address _instance, uint32 _block) = (hashes[i], instances[i], blocks[i]); + assembly { + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x38), _block) + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x34), _instance) + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x20), _hash) + } + } + uint256 gasHash = gasleft(); + bytes32 hash1 = sha256(data); + return (gasleft() - gasHash, gasHash - gasBefore, hash1); + } +} diff --git a/contracts/mocks/PublicArray.sol b/contracts/mocks/PublicArray.sol new file mode 100644 index 0000000..173ccb7 --- /dev/null +++ b/contracts/mocks/PublicArray.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +contract PublicArray { + uint256[] public deposits; + uint256[] public withdrawals; + + function lastProcessedDepositLeaf() external view returns (uint256) {} + + function lastProcessedWithdrawalLeaf() external view returns (uint256) {} + + function depositRoot() external view returns (bytes32) {} + + function withdrawalRoot() external view returns (bytes32) {} + + function setDeposits(uint256[] memory _deposits) public { + for (uint256 i = 0; i < _deposits.length; i++) { + deposits.push(_deposits[i]); + } + } + + function setWithdrawals(uint256[] memory _withdrawals) public { + for (uint256 i = 0; i < _withdrawals.length; i++) { + withdrawals.push(_withdrawals[i]); + } + } +} diff --git a/contracts/mocks/TornadoTreesMock.sol b/contracts/mocks/TornadoTreesMock.sol new file mode 100644 index 0000000..70d2f35 --- /dev/null +++ b/contracts/mocks/TornadoTreesMock.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + +import "../TornadoTrees.sol"; +import "../interfaces/ITornadoTreesV1.sol"; +import "../interfaces/IBatchTreeUpdateVerifier.sol"; + +contract TornadoTreesMock is TornadoTrees { + uint256 public currentBlock; + + constructor( + address _governance, + ITornadoTreesV1 _tornadoTreesV1, + SearchParams memory _searchParams + ) public TornadoTrees(_governance, _tornadoTreesV1, _searchParams) {} + + function setBlockNumber(uint256 _blockNumber) public { + currentBlock = _blockNumber; + } + + function blockNumber() public view override returns (uint256) { + return currentBlock == 0 ? block.number : currentBlock; + } + + function findArrayLengthMock( + ITornadoTreesV1 _tornadoTreesV1, + string memory _type, + uint256 _from, + uint256 _step + ) public view returns (uint256) { + return findArrayLength(_tornadoTreesV1, _type, _from, _step); + } + + function register( + address _instance, + bytes32 _commitment, + bytes32 _nullifier, + uint256 _depositBlockNumber, + uint256 _withdrawBlockNumber + ) public { + setBlockNumber(_depositBlockNumber); + registerDeposit(_instance, _commitment); + + setBlockNumber(_withdrawBlockNumber); + registerWithdrawal(_instance, _nullifier); + } + + function updateRoots(bytes32 _depositRoot, bytes32 _withdrawalRoot) public { + depositRoot = _depositRoot; + withdrawalRoot = _withdrawalRoot; + } + + function updateDepositTreeMock( + bytes32 _oldRoot, + bytes32 _newRoot, + uint32 _pathIndices, + TreeLeaf[] calldata _events + ) public pure returns (uint256) { + bytes memory data = new bytes(BYTES_SIZE); + assembly { + mstore(add(data, 0x44), _pathIndices) + mstore(add(data, 0x40), _newRoot) + mstore(add(data, 0x20), _oldRoot) + } + for (uint256 i = 0; i < CHUNK_SIZE; i++) { + (bytes32 hash, address instance, uint32 depositBlock) = (_events[i].hash, _events[i].instance, _events[i].block); + assembly { + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x7c), depositBlock) + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x78), instance) + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x64), hash) + } + } + return uint256(sha256(data)) % SNARK_FIELD; + } + + function updateDepositTreeMock2( + bytes32 _oldRoot, + bytes32 _newRoot, + uint32 _pathIndices, + TreeLeaf[] calldata _events + ) public pure returns (bytes memory) { + bytes memory data = new bytes(BYTES_SIZE); + assembly { + mstore(add(data, 0x44), _pathIndices) + mstore(add(data, 0x40), _newRoot) + mstore(add(data, 0x20), _oldRoot) + } + for (uint256 i = 0; i < CHUNK_SIZE; i++) { + (bytes32 hash, address instance, uint32 depositBlock) = (_events[i].hash, _events[i].instance, _events[i].block); + assembly { + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x7c), depositBlock) + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x78), instance) + mstore(add(add(data, mul(ITEM_SIZE, i)), 0x64), hash) + } + } + return data; + } +} diff --git a/contracts/mocks/TornadoTreesV1Mock.sol b/contracts/mocks/TornadoTreesV1Mock.sol new file mode 100644 index 0000000..c3e3eaf --- /dev/null +++ b/contracts/mocks/TornadoTreesV1Mock.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + +contract TornadoTreesV1Mock { + uint256 public timestamp; + uint256 public currentBlock; + + bytes32[] public deposits; + uint256 public lastProcessedDepositLeaf; + + bytes32[] public withdrawals; + uint256 public lastProcessedWithdrawalLeaf; + + bytes32 public depositRoot; + bytes32 public withdrawalRoot; + + constructor( + uint256 _lastProcessedDepositLeaf, + uint256 _lastProcessedWithdrawalLeaf, + bytes32 _depositRoot, + bytes32 _withdrawalRoot + ) public { + lastProcessedDepositLeaf = _lastProcessedDepositLeaf; + lastProcessedWithdrawalLeaf = _lastProcessedWithdrawalLeaf; + depositRoot = _depositRoot; + withdrawalRoot = _withdrawalRoot; + } + + function register( + address _instance, + bytes32 _commitment, + bytes32 _nullifier, + uint256 _depositBlockNumber, + uint256 _withdrawBlockNumber + ) public { + setBlockNumber(_depositBlockNumber); + deposits.push(keccak256(abi.encode(_instance, _commitment, blockNumber()))); + setBlockNumber(_withdrawBlockNumber); + withdrawals.push(keccak256(abi.encode(_instance, _nullifier, blockNumber()))); + } + + function getRegisteredDeposits() external view returns (bytes32[] memory _deposits) { + uint256 count = deposits.length - lastProcessedDepositLeaf; + _deposits = new bytes32[](count); + for (uint256 i = 0; i < count; i++) { + _deposits[i] = deposits[lastProcessedDepositLeaf + i]; + } + } + + function getRegisteredWithdrawals() external view returns (bytes32[] memory _withdrawals) { + uint256 count = withdrawals.length - lastProcessedWithdrawalLeaf; + _withdrawals = new bytes32[](count); + for (uint256 i = 0; i < count; i++) { + _withdrawals[i] = withdrawals[lastProcessedWithdrawalLeaf + i]; + } + } + + function setLastProcessedDepositLeaf(uint256 _lastProcessedDepositLeaf) public { + lastProcessedDepositLeaf = _lastProcessedDepositLeaf; + } + + function setLastProcessedWithdrawalLeaf(uint256 _lastProcessedWithdrawalLeaf) public { + lastProcessedWithdrawalLeaf = _lastProcessedWithdrawalLeaf; + } + + function resolve(bytes32 _addr) public pure returns (address) { + return address(uint160(uint256(_addr) >> (12 * 8))); + } + + function setBlockNumber(uint256 _blockNumber) public { + currentBlock = _blockNumber; + } + + function blockNumber() public view returns (uint256) { + return currentBlock == 0 ? block.number : currentBlock; + } +} 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/hardhat.config.js b/hardhat.config.js new file mode 100644 index 0000000..b177477 --- /dev/null +++ b/hardhat.config.js @@ -0,0 +1,46 @@ +/* global task, ethers */ +require('@nomiclabs/hardhat-waffle') +require('dotenv').config() +// This is a sample Hardhat task. To learn how to create your own go to +// https://hardhat.org/guides/create-task.html +task('accounts', 'Prints the list of accounts', async () => { + const accounts = await ethers.getSigners() + + for (const account of accounts) { + console.log(account.address) + } +}) + +// You need to export an object to set up your config +// Go to https://hardhat.org/config/ to learn more + +/** + * @type import('hardhat/config').HardhatUserConfig + */ +const config = { + solidity: { + version: '0.6.12', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + networks: { + hardhat: { + blockGasLimit: 9500000, + }, + }, + mocha: { + timeout: 600000, + }, +} + +if (process.env.NETWORK) { + config.networks[process.env.NETWORK] = { + url: `https://${process.env.NETWORK}.infura.io/v3/${process.env.INFURA_TOKEN}`, + accounts: [process.env.PRIVATE_KEY], + } +} +module.exports = config diff --git a/optimize/Dockerfile b/optimize/Dockerfile new file mode 100644 index 0000000..753daec --- /dev/null +++ b/optimize/Dockerfile @@ -0,0 +1,47 @@ +FROM ubuntu + +RUN apt-get update && \ + apt-get install -y python3 python3-distutils g++ make curl git && \ + rm -rf /var/lib/apt/lists/* + +# Install nvm with node and npm +RUN rm /bin/sh && ln -s /bin/bash /bin/sh +ENV NVM_DIR /usr/local/nvm +ENV NODE_VERSION 14.8.0 +RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.30.1/install.sh | bash \ + && source $NVM_DIR/nvm.sh \ + && nvm install $NODE_VERSION \ + && nvm alias default $NODE_VERSION \ + && nvm use default +ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules +ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH +RUN node --version + +WORKDIR /root + +RUN git clone https://github.com/nodejs/node.git +RUN git clone https://github.com/iden3/circom.git + +COPY node.sh /tmp + +RUN apt-get update && apt-get install -y ninja-build +RUN /tmp/node.sh + +RUN cd circom && \ + git checkout v0.5.35 && \ + npm install + +RUN git clone https://github.com/iden3/r1csoptimize +RUN cd r1csoptimize && \ + git checkout 8bc528b06c0f98818d1b5224e2078397f0bb7faf && \ + npm install + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +RUN source $HOME/.cargo/env && cargo install zkutil +RUN npm install -g circom snarkjs + +WORKDIR /root/test +RUN npm init -y && npm install circomlib +RUN apt-get update && apt-get install -y ne +RUN mkdir circuits +COPY sha/circuit.circom sha/input.js test.sh ./circuits/ diff --git a/optimize/node.sh b/optimize/node.sh new file mode 100755 index 0000000..5428ba0 --- /dev/null +++ b/optimize/node.sh @@ -0,0 +1,34 @@ +#!/bin/bash -e +cd node +git checkout 8beef5eeb82425b13d447b50beafb04ece7f91b1 +patch -p1 <(this)->heap(); + if (heap->gc_state() != i::Heap::NOT_IN_GC) return; +- heap->ReportExternalMemoryPressure(); ++ // heap->ReportExternalMemoryPressure(); + } + + HeapProfiler* Isolate::GetHeapProfiler() { +diff --git a/deps/v8/src/objects/backing-store.cc b/deps/v8/src/objects/backing-store.cc +index bd9f39b7d3..c7d7e58ef3 100644 +--- a/deps/v8/src/objects/backing-store.cc ++++ b/deps/v8/src/objects/backing-store.cc +@@ -34,7 +34,7 @@ constexpr bool kUseGuardRegions = false; + // address space limits needs to be smaller. + constexpr size_t kAddressSpaceLimit = 0x8000000000L; // 512 GiB + #elif V8_TARGET_ARCH_64_BIT +-constexpr size_t kAddressSpaceLimit = 0x10100000000L; // 1 TiB + 4 GiB ++constexpr size_t kAddressSpaceLimit = 0x40100000000L; // 4 TiB + 4 GiB + #else + constexpr size_t kAddressSpaceLimit = 0xC0000000; // 3 GiB + #endif +EOL +# ./configure --ninja +# JOBS=24 make +./configure +make -j12 \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..809a64b --- /dev/null +++ b/package.json @@ -0,0 +1,56 @@ +{ + "name": "tornado-trees", + "version": "0.0.9", + "main": "src/index.js", + "repository": "https://github.com/tornadocash/tornado-trees.git", + "author": "Tornadocash team ", + "license": "MIT", + "files": [ + "src/*", + "contracts/*", + "scripts/*" + ], + "scripts": { + "compile": "npx hardhat compile", + "test": "npx hardhat test", + "eslint": "eslint --ext .js --ignore-path .gitignore .", + "prettier:check": "prettier --check . --config .prettierrc", + "prettier:fix": "prettier --write . --config .prettierrc", + "lint": "yarn eslint && yarn prettier:check", + "changeTreeHeight": "scripts/changeTreeHeight.sh", + "circuit:batchTreeUpdate": "scripts/buildCircuit.sh BatchTreeUpdate", + "circuit:batchTreeUpdateLarge": "scripts/buildCircuit.sh BatchTreeUpdate large", + "circuit:batchTreeUpdateWitness": "scripts/buildWitness.sh BatchTreeUpdate", + "circuit": "yarn circuit:batchTreeUpdate" + }, + "devDependencies": { + "@nomiclabs/hardhat-ethers": "^2.0.1", + "@nomiclabs/hardhat-waffle": "^2.0.1", + "babel-eslint": "^10.1.0", + "chai": "^4.2.0", + "eslint": "^7.19.0", + "eslint-config-prettier": "^7.2.0", + "eslint-plugin-prettier": "^3.3.1", + "ethereum-waffle": "^3.2.2", + "ethers": "^5.0.26", + "hardhat": "^2.0.8", + "prettier": "^2.2.1", + "prettier-plugin-solidity": "^1.0.0-beta.3", + "solhint-plugin-prettier": "^0.0.5", + "torn-token": "^1.0.0" + }, + "dependencies": { + "@openzeppelin/contracts": "^3.4.0", + "@openzeppelin/upgrades-core": "^1.5.1", + "circom": "^0.5.38", + "circom_runtime": "^0.1.12", + "circomlib": "git+https://github.com/tornadocash/circomlib.git#d20d53411d1bef61f38c99a8b36d5d0cc4836aa1", + "dotenv": "^8.2.0", + "ffiasm": "^0.1.1", + "ffjavascript": "^0.2.35", + "fixed-merkle-tree": "^0.5.0", + "jssha": "^3.2.0", + "snarkjs": "^0.3.57", + "tmp-promise": "^3.0.2" + } +} diff --git a/scripts/buildCircuit.sh b/scripts/buildCircuit.sh new file mode 100755 index 0000000..acea678 --- /dev/null +++ b/scripts/buildCircuit.sh @@ -0,0 +1,11 @@ +#!/bin/bash -e +mkdir -p artifacts/circuits +if [ "$2" = "large" ]; then + 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 artifacts/circuits/$1.r1cs -w artifacts/circuits/$1.wasm -s artifacts/circuits/$1.sym circuits/$1.circom +fi +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/scripts/buildWitness.sh b/scripts/buildWitness.sh new file mode 100755 index 0000000..69387cf --- /dev/null +++ b/scripts/buildWitness.sh @@ -0,0 +1,8 @@ +#!/bin/bash -e +# required dependencies: libgmp-dev nlohmann-json3-dev nasm g++ +cd artifacts/circuits +node ../../node_modules/ffiasm/src/buildzqfield.js -q 21888242871839275222246405745257275088548364400416034343698204186575808495617 -n Fr +nasm -felf64 fr.asm +cp ../../node_modules/circom_runtime/c/*.cpp ./ +cp ../../node_modules/circom_runtime/c/*.hpp ./ +g++ -pthread main.cpp calcwit.cpp utils.cpp fr.cpp fr.o ${1}.cpp -o ${1} -lgmp -std=c++11 -O3 -fopenmp -DSANITY_CHECK diff --git a/scripts/changeTreeHeight.sh b/scripts/changeTreeHeight.sh new file mode 100755 index 0000000..003e8f3 --- /dev/null +++ b/scripts/changeTreeHeight.sh @@ -0,0 +1,7 @@ +#!/bin/bash +case $(sed --help 2>&1) in + *GNU*) sed_i () { xargs sed -i "$@"; };; + *) sed_i () { xargs sed -i '' "$@"; };; +esac + +grep -l --exclude-dir={.git,node_modules,artifacts} -r "CHUNK_TREE_HEIGHT = [0-9]" . | sed_i "s/CHUNK_TREE_HEIGHT = [0-9]/CHUNK_TREE_HEIGHT = ${1}/g" diff --git a/scripts/deploy.js b/scripts/deploy.js new file mode 100644 index 0000000..b4f057b --- /dev/null +++ b/scripts/deploy.js @@ -0,0 +1,114 @@ +// We require the Hardhat Runtime Environment explicitly here. This is optional +// but useful for running the script in a standalone fashion through `node