Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf5581f764 | ||
|
|
0cabd318cd | ||
|
|
2dd8c95300 | ||
|
|
7abc834475 | ||
|
|
4f732d05bc | ||
|
|
93dd5d5e86 | ||
|
|
06244af218 | ||
|
|
2bcb37c628 | ||
|
|
59f13c3aaa | ||
|
|
2667feb4f1 | ||
|
|
a03efa767c | ||
|
|
11c48fae14 | ||
|
|
fe2912fc33 | ||
|
|
9fdafd2765 | ||
|
|
6450153f2b | ||
|
|
6f42be6213 | ||
|
|
6a01ff9642 | ||
|
|
592d3e112d | ||
|
|
a4b98dd195 |
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -15,8 +15,9 @@ jobs:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
- run: yarn install
|
||||
- run: cargo install zkutil
|
||||
- run: yarn install
|
||||
- run: yarn changeTreeHeight 2
|
||||
- run: yarn circuit
|
||||
- run: yarn test
|
||||
- run: yarn lint
|
||||
|
||||
@@ -16,7 +16,6 @@ COPY circuits circuits
|
||||
COPY scripts scripts
|
||||
# ENV NODE_OPTIONS='--trace-gc --trace-gc-ignore-scavenger --max-old-space-size=2048000 --initial-old-space-size=2048000 --no-global-gc-scheduling --no-incremental-marking --max-semi-space-size=1024 --initial-heap-size=2048000'
|
||||
ENV NODE_OPTIONS='--max-old-space-size=2048000'
|
||||
RUN mkdir -p build/circuits
|
||||
RUN yarn circuit:batchTreeUpdateLarge
|
||||
RUN yarn circuit:batchTreeUpdateWitness
|
||||
COPY . .
|
||||
|
||||
20
README.md
20
README.md
@@ -15,3 +15,23 @@ $ yarn
|
||||
$ yarn circuit
|
||||
$ yarn test
|
||||
```
|
||||
|
||||
## Mainnet testing
|
||||
|
||||
```bash
|
||||
$ yarn circuit
|
||||
$ npx hardhat node --fork <https://eth-mainnet.alchemyapi.io/v2/API_KEY> --fork-block-number 11827889
|
||||
$ npx hardhat test
|
||||
```
|
||||
|
||||
## Checklist for batch size changing
|
||||
|
||||
find and replace the `CHUNK_TREE_HEIGHT = ` in following files
|
||||
|
||||
1. `circuits/BatchTreeUpdate.circom`
|
||||
2. `contracts/TornadoTrees.sol`
|
||||
3. `tornadoTrees.test.js`
|
||||
|
||||
## build large circuits
|
||||
|
||||
1. docker build . -t tornadocash/tornado-trees
|
||||
|
||||
@@ -74,20 +74,18 @@ template BatchTreeUpdate(levels, batchLevels, zeroBatchLeaf) {
|
||||
|
||||
// zeroLeaf = keccak256("tornado") % FIELD_SIZE
|
||||
// zeroBatchLeaf is poseidon(zeroLeaf, zeroLeaf) (batchLevels - 1) times
|
||||
component main = BatchTreeUpdate(20, 2, 21572503925325825116380792768937986743990254033176521064707045559165336555197)
|
||||
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;
|
||||
}
|
||||
|
||||
// for mainnet use 20, 7, 17278668323652664881420209773995988768195998574629614593395162463145689805534
|
||||
|
||||
/*
|
||||
zeros of n-th order:
|
||||
21663839004416932945382355908790599225266501822907911457504978515578255421292
|
||||
11850551329423159860688778991827824730037759162201783566284850822760196767874
|
||||
21572503925325825116380792768937986743990254033176521064707045559165336555197
|
||||
11224495635916644180335675565949106569141882748352237685396337327907709534945
|
||||
2399242030534463392142674970266584742013168677609861039634639961298697064915
|
||||
13182067204896548373877843501261957052850428877096289097123906067079378150834
|
||||
7106632500398372645836762576259242192202230138343760620842346283595225511823
|
||||
17278668323652664881420209773995988768195998574629614593395162463145689805534
|
||||
209436188287252095316293336871467217491997565239632454977424802439169726471
|
||||
6509061943359659796226067852175931816441223836265895622135845733346450111408
|
||||
*/
|
||||
var CHUNK_TREE_HEIGHT = 8
|
||||
component main = BatchTreeUpdate(20, CHUNK_TREE_HEIGHT, nthZero(CHUNK_TREE_HEIGHT))
|
||||
|
||||
@@ -14,34 +14,43 @@ template TreeUpdateArgsHasher(nLeaves) {
|
||||
var bitsPerLeaf = 160 + 256 + 32;
|
||||
component hasher = Sha256(header + nLeaves * bitsPerLeaf);
|
||||
|
||||
component bitsOldRoot = Num2Bits(256);
|
||||
component bitsNewRoot = Num2Bits(256);
|
||||
// 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;
|
||||
for(var i = 0; i < 256; i++) {
|
||||
hasher.in[i] <== bitsOldRoot.out[255 - i];
|
||||
|
||||
hasher.in[0] <== 0;
|
||||
hasher.in[1] <== 0;
|
||||
for(var i = 0; i < 254; i++) {
|
||||
hasher.in[i + 2] <== bitsOldRoot.out[253 - i];
|
||||
}
|
||||
for(var i = 0; i < 256; i++) {
|
||||
hasher.in[i + 256] <== bitsNewRoot.out[255 - 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++) {
|
||||
bitsHash[leaf] = Num2Bits(256);
|
||||
// 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];
|
||||
for(var i = 0; i < 256; i++) {
|
||||
hasher.in[header + leaf * bitsPerLeaf + i] <== bitsHash[leaf].out[255 - i];
|
||||
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];
|
||||
@@ -55,4 +64,4 @@ template TreeUpdateArgsHasher(nLeaves) {
|
||||
b2n.in[i] <== hasher.out[255 - i];
|
||||
}
|
||||
out <== b2n.out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ pragma solidity ^0.6.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./interfaces/ITornadoTreesV1.sol";
|
||||
import "./interfaces/IVerifier.sol";
|
||||
import "./interfaces/IBatchTreeUpdateVerifier.sol";
|
||||
|
||||
contract TornadoTrees {
|
||||
address public immutable governance;
|
||||
@@ -13,11 +13,11 @@ contract TornadoTrees {
|
||||
bytes32 public withdrawalRoot;
|
||||
bytes32 public previousWithdrawalRoot;
|
||||
address public tornadoProxy;
|
||||
IVerifier public treeUpdateVerifier;
|
||||
IBatchTreeUpdateVerifier public treeUpdateVerifier;
|
||||
ITornadoTreesV1 public immutable tornadoTreesV1;
|
||||
|
||||
// make sure CHUNK_TREE_HEIGHT has the same value in BatchTreeUpdate.circom
|
||||
uint256 public constant CHUNK_TREE_HEIGHT = 2;
|
||||
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;
|
||||
@@ -26,7 +26,7 @@ contract TornadoTrees {
|
||||
mapping(uint256 => bytes32) public deposits;
|
||||
uint256 public depositsLength;
|
||||
uint256 public lastProcessedDepositLeaf;
|
||||
uint256 public immutable depositV1Length;
|
||||
uint256 public immutable depositsV1Length;
|
||||
|
||||
mapping(uint256 => bytes32) public withdrawals;
|
||||
uint256 public withdrawalsLength;
|
||||
@@ -42,13 +42,6 @@ contract TornadoTrees {
|
||||
uint32 block;
|
||||
}
|
||||
|
||||
struct Batch {
|
||||
bytes32 oldRoot;
|
||||
bytes32 newRoot;
|
||||
uint8 pathIndices;
|
||||
TreeLeaf[CHUNK_SIZE] events;
|
||||
}
|
||||
|
||||
modifier onlyTornadoProxy {
|
||||
require(msg.sender == tornadoProxy, "Not authorized");
|
||||
_;
|
||||
@@ -60,17 +53,17 @@ contract TornadoTrees {
|
||||
}
|
||||
|
||||
struct SearchParams {
|
||||
uint256 unprocessedDeposits;
|
||||
uint256 unprocessedWithdrawals;
|
||||
uint256 depositsPerDay;
|
||||
uint256 withdrawalsPerDay;
|
||||
uint256 depositsFrom;
|
||||
uint256 depositsStep;
|
||||
uint256 withdrawalsFrom;
|
||||
uint256 withdrawalsStep;
|
||||
}
|
||||
|
||||
constructor(
|
||||
address _governance,
|
||||
address _tornadoProxy,
|
||||
ITornadoTreesV1 _tornadoTreesV1,
|
||||
IVerifier _treeUpdateVerifier,
|
||||
IBatchTreeUpdateVerifier _treeUpdateVerifier,
|
||||
SearchParams memory _searchParams
|
||||
) public {
|
||||
governance = _governance;
|
||||
@@ -82,11 +75,11 @@ contract TornadoTrees {
|
||||
uint256 lastDepositLeaf = _tornadoTreesV1.lastProcessedDepositLeaf();
|
||||
require(lastDepositLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state");
|
||||
lastProcessedDepositLeaf = lastDepositLeaf;
|
||||
depositsLength = depositV1Length = findArrayLength(
|
||||
depositsLength = depositsV1Length = findArrayLength(
|
||||
_tornadoTreesV1,
|
||||
"deposits(uint256)",
|
||||
_searchParams.unprocessedDeposits,
|
||||
_searchParams.depositsPerDay
|
||||
_searchParams.depositsFrom,
|
||||
_searchParams.depositsStep
|
||||
);
|
||||
|
||||
withdrawalRoot = _tornadoTreesV1.withdrawalRoot();
|
||||
@@ -96,8 +89,8 @@ contract TornadoTrees {
|
||||
withdrawalsLength = withdrawalsV1Length = findArrayLength(
|
||||
_tornadoTreesV1,
|
||||
"withdrawals(uint256)",
|
||||
_searchParams.unprocessedWithdrawals,
|
||||
_searchParams.withdrawalsPerDay
|
||||
_searchParams.withdrawalsFrom,
|
||||
_searchParams.withdrawalsStep
|
||||
);
|
||||
}
|
||||
|
||||
@@ -110,6 +103,9 @@ contract TornadoTrees {
|
||||
uint256 _from, // most likely array length after the proposal has passed
|
||||
uint256 _step // optimal step size to find first match, approximately equals dispersion
|
||||
) public 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 {
|
||||
@@ -176,14 +172,14 @@ contract TornadoTrees {
|
||||
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 >= depositV1Length ? deposits[offset + i] : tornadoTreesV1.deposits(offset + i);
|
||||
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 >= depositV1Length) {
|
||||
if (offset + i >= depositsV1Length) {
|
||||
delete deposits[offset + i];
|
||||
} else {
|
||||
emit DepositData(instance, hash, blockNumber, offset + i);
|
||||
@@ -271,7 +267,7 @@ contract TornadoTrees {
|
||||
tornadoProxy = _tornadoProxy;
|
||||
}
|
||||
|
||||
function setVerifierContract(IVerifier _treeUpdateVerifier) external onlyGovernance {
|
||||
function setVerifierContract(IBatchTreeUpdateVerifier _treeUpdateVerifier) external onlyGovernance {
|
||||
treeUpdateVerifier = _treeUpdateVerifier;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
pragma solidity ^0.6.0;
|
||||
|
||||
interface IVerifier {
|
||||
interface IBatchTreeUpdateVerifier {
|
||||
function verifyProof(bytes calldata proof, uint256[1] calldata input) external view returns (bool);
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
pragma solidity ^0.6.0;
|
||||
|
||||
contract Pack {
|
||||
uint256 public constant CHUNK_TREE_HEIGHT = 7;
|
||||
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;
|
||||
|
||||
@@ -5,7 +5,7 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../TornadoTrees.sol";
|
||||
import "../interfaces/ITornadoTreesV1.sol";
|
||||
import "../interfaces/IVerifier.sol";
|
||||
import "../interfaces/IBatchTreeUpdateVerifier.sol";
|
||||
|
||||
contract TornadoTreesMock is TornadoTrees {
|
||||
uint256 public currentBlock;
|
||||
@@ -14,7 +14,7 @@ contract TornadoTreesMock is TornadoTrees {
|
||||
address _governance,
|
||||
address _tornadoProxy,
|
||||
ITornadoTreesV1 _tornadoTreesV1,
|
||||
IVerifier _treeUpdateVerifier,
|
||||
IBatchTreeUpdateVerifier _treeUpdateVerifier,
|
||||
SearchParams memory _searchParams
|
||||
) public TornadoTrees(_governance, _tornadoProxy, _tornadoTreesV1, _treeUpdateVerifier, _searchParams) {}
|
||||
|
||||
@@ -40,6 +40,11 @@ contract TornadoTreesMock is TornadoTrees {
|
||||
registerWithdrawal(_instance, _nullifier);
|
||||
}
|
||||
|
||||
function updateRoots(bytes32 _depositRoot, bytes32 _withdrawalRoot) public {
|
||||
depositRoot = _depositRoot;
|
||||
withdrawalRoot = _withdrawalRoot;
|
||||
}
|
||||
|
||||
function updateDepositTreeMock(
|
||||
bytes32 _oldRoot,
|
||||
bytes32 _newRoot,
|
||||
|
||||
@@ -18,7 +18,15 @@ task('accounts', 'Prints the list of accounts', async () => {
|
||||
* @type import('hardhat/config').HardhatUserConfig
|
||||
*/
|
||||
const config = {
|
||||
solidity: '0.6.12',
|
||||
solidity: {
|
||||
version: '0.6.12',
|
||||
settings: {
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
networks: {
|
||||
hardhat: {
|
||||
blockGasLimit: 9500000,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tornado-trees",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.6",
|
||||
"main": "src/index.js",
|
||||
"repository": "https://github.com/tornadocash/tornado-trees.git",
|
||||
"author": "Tornadocash team <hello@tornado.cash>",
|
||||
@@ -16,6 +16,7 @@
|
||||
"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",
|
||||
|
||||
@@ -3,7 +3,7 @@ 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
|
||||
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
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash -e
|
||||
# required dependencies: libgmp-dev nlohmann-json3-dev nasm g++
|
||||
cd build/circuits
|
||||
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
|
||||
g++ -pthread main.cpp calcwit.cpp utils.cpp fr.cpp fr.o ${1}.cpp -o ${1} -lgmp -std=c++11 -O3 -fopenmp -DSANITY_CHECK
|
||||
|
||||
7
scripts/changeTreeHeight.sh
Executable file
7
scripts/changeTreeHeight.sh
Executable file
@@ -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"
|
||||
@@ -15,7 +15,7 @@ const instances = [
|
||||
]
|
||||
|
||||
const blocks = ['0xaaaaaaaa', '0xbbbbbbbb', '0xcccccccc', '0xdddddddd']
|
||||
const CHUNK_TREE_HEIGHT = 2
|
||||
const CHUNK_TREE_HEIGHT = 8
|
||||
const levels = 20
|
||||
|
||||
const nonRandomBN = (nonce = 0) =>
|
||||
|
||||
115
scripts/deployEmptyV1.js
Normal file
115
scripts/deployEmptyV1.js
Normal file
@@ -0,0 +1,115 @@
|
||||
// We require the Hardhat Runtime Environment explicitly here. This is optional
|
||||
// but useful for running the script in a standalone fashion through `node <script>`.
|
||||
//
|
||||
// When running the script with `hardhat run <script>` you'll find the Hardhat
|
||||
// Runtime Environment's members available in the global scope.
|
||||
const hre = require('hardhat')
|
||||
const { toFixedHex, poseidonHash2 } = require('../src/utils')
|
||||
const MerkleTree = require('fixed-merkle-tree')
|
||||
const abi = new hre.ethers.utils.AbiCoder()
|
||||
const instances = [
|
||||
'0x1111000000000000000000000000000000001111',
|
||||
'0x2222000000000000000000000000000000002222',
|
||||
'0x3333000000000000000000000000000000003333',
|
||||
'0x4444000000000000000000000000000000004444',
|
||||
]
|
||||
|
||||
const blocks = ['0xaaaaaaaa', '0xbbbbbbbb', '0xcccccccc', '0xdddddddd']
|
||||
const CHUNK_TREE_HEIGHT = 8
|
||||
const levels = 20
|
||||
|
||||
const nonRandomBN = (nonce = 0) =>
|
||||
hre.ethers.BigNumber.from('0x004d51bffaafdb3eed0661c1cfd76c8cd6ec1456b80b24bbb855f3a141ebf0be').sub(nonce)
|
||||
|
||||
async function main() {
|
||||
const governance = '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce'
|
||||
const [tornadoProxy] = await hre.ethers.getSigners()
|
||||
console.log('deployer/tornadoProxy acc: ', tornadoProxy.address)
|
||||
|
||||
const tree = new MerkleTree(levels, [], { hashFunction: poseidonHash2 })
|
||||
const TornadoTreesV1Mock = await hre.ethers.getContractFactory('TornadoTreesV1Mock')
|
||||
const tornadoTreesV1Mock = await TornadoTreesV1Mock.deploy(0, 0, tree.root(), tree.root())
|
||||
await tornadoTreesV1Mock.deployed()
|
||||
console.log('tornadoTreesV1Mock deployed to:', tornadoTreesV1Mock.address)
|
||||
|
||||
const BatchTreeUpdateVerifier = await hre.ethers.getContractFactory('BatchTreeUpdateVerifier')
|
||||
const verifier = await BatchTreeUpdateVerifier.deploy()
|
||||
await verifier.deployed()
|
||||
console.log('Verifier deployed to:', verifier.address)
|
||||
|
||||
const TornadoTrees = await hre.ethers.getContractFactory('TornadoTreesMock')
|
||||
const tornadoTrees = await TornadoTrees.deploy(
|
||||
governance,
|
||||
tornadoProxy.address,
|
||||
tornadoTreesV1Mock.address,
|
||||
verifier.address,
|
||||
{
|
||||
depositsFrom: 0,
|
||||
depositsStep: 0,
|
||||
withdrawalsFrom: 0,
|
||||
withdrawalsStep: 0,
|
||||
},
|
||||
)
|
||||
await tornadoTrees.deployed()
|
||||
|
||||
const notes = []
|
||||
const depositEvents = {}
|
||||
const withdrawalEvents = {}
|
||||
for (let i = 0; i < 2 ** CHUNK_TREE_HEIGHT; i++) {
|
||||
const note = {
|
||||
instance: instances[i % instances.length],
|
||||
depositBlock: blocks[i % blocks.length],
|
||||
withdrawalBlock: 2 + i + i * 4 * 60 * 24,
|
||||
commitment: nonRandomBN(i),
|
||||
nullifierHash: nonRandomBN(i + instances.length),
|
||||
}
|
||||
|
||||
await tornadoTrees.register(
|
||||
note.instance,
|
||||
toFixedHex(note.commitment),
|
||||
toFixedHex(note.nullifierHash),
|
||||
note.depositBlock,
|
||||
note.withdrawalBlock,
|
||||
{ gasLimit: 200000 },
|
||||
)
|
||||
const encodedData = abi.encode(
|
||||
['address', 'bytes32', 'uint256'],
|
||||
[note.instance, toFixedHex(note.commitment), note.depositBlock],
|
||||
)
|
||||
const leaf = hre.ethers.utils.keccak256(encodedData)
|
||||
depositEvents[leaf] = {
|
||||
hash: toFixedHex(note.commitment),
|
||||
instance: toFixedHex(note.instance, 20),
|
||||
block: toFixedHex(note.depositBlock, 4),
|
||||
}
|
||||
const encodedDataW = abi.encode(
|
||||
['address', 'bytes32', 'uint256'],
|
||||
[note.instance, toFixedHex(note.nullifierHash), note.withdrawalBlock],
|
||||
)
|
||||
const leafW = hre.ethers.utils.keccak256(encodedDataW)
|
||||
withdrawalEvents[leafW] = {
|
||||
hash: toFixedHex(note.nullifierHash),
|
||||
instance: toFixedHex(note.instance, 20),
|
||||
block: toFixedHex(note.withdrawalBlock, 4),
|
||||
}
|
||||
notes[i] = note
|
||||
}
|
||||
console.log(`Registered ${notes.length} new deposits and withdrawals in tornadoTreesV1Mock`)
|
||||
console.log(JSON.stringify(depositEvents, null, 2))
|
||||
console.log(JSON.stringify(withdrawalEvents, null, 2))
|
||||
|
||||
console.log('tornadoTrees deployed to:', tornadoTrees.address)
|
||||
console.log('You can use the same private key to register new deposits in the tornadoTrees')
|
||||
|
||||
console.log(`\nTORNADO_TREES_V1=${tornadoTreesV1Mock.address}`)
|
||||
console.log(`TORNADO_TREES=${tornadoTrees.address}`)
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -55,9 +55,9 @@ function prove(input, keyBasePath) {
|
||||
}
|
||||
|
||||
function batchTreeUpdate(tree, events) {
|
||||
const batchHeight = 2 //await this.tornadoTreesContract.CHUNK_TREE_HEIGHT()
|
||||
if (events.length !== 1 << batchHeight) {
|
||||
throw new Error('events length does not match the batch size')
|
||||
const batchHeight = Math.log2(events.length)
|
||||
if (!Number.isInteger(batchHeight)) {
|
||||
throw new Error('events length has to be power of 2')
|
||||
}
|
||||
|
||||
const oldRoot = tree.root().toString()
|
||||
|
||||
@@ -24,10 +24,10 @@ describe('findArrayLength', () => {
|
||||
publicArray.address,
|
||||
publicArray.address,
|
||||
{
|
||||
unprocessedDeposits: 3,
|
||||
unprocessedWithdrawals: 3,
|
||||
depositsPerDay: 2,
|
||||
withdrawalsPerDay: 2,
|
||||
depositsFrom: 3,
|
||||
depositsStep: 3,
|
||||
withdrawalsFrom: 2,
|
||||
withdrawalsStep: 2,
|
||||
},
|
||||
)
|
||||
})
|
||||
@@ -37,6 +37,13 @@ describe('findArrayLength', () => {
|
||||
expect(depositsLength).to.be.equal(depositsEven.length)
|
||||
})
|
||||
|
||||
it('should work for empty array', async () => {
|
||||
publicArray = await PublicArray.deploy()
|
||||
// will throw out of gas if you pass non zero params
|
||||
const depositsLength = await tornadoTrees.findArrayLength(publicArray.address, 'deposits(uint256)', 0, 0)
|
||||
expect(depositsLength).to.be.equal(0)
|
||||
})
|
||||
|
||||
it('should work for odd array', async () => {
|
||||
publicArray = await PublicArray.deploy()
|
||||
await publicArray.setDeposits(depositsOdd)
|
||||
|
||||
@@ -13,7 +13,7 @@ const hashes = [
|
||||
'0x57f7b90a3cb4ea6860e6dd5fa44ac4f53ebe6ae3948af577a01ef51738313246',
|
||||
]
|
||||
|
||||
const CHUNK_TREE_HEIGHT = 7
|
||||
const CHUNK_TREE_HEIGHT = 8
|
||||
describe.skip('Pack', () => {
|
||||
it('should work', async () => {
|
||||
const Pack = await ethers.getContractFactory('Pack')
|
||||
|
||||
@@ -4,7 +4,7 @@ const { poseidonHash2, randomBN } = require('../src/utils')
|
||||
const { batchTreeUpdate, prove } = require('../src/index')
|
||||
|
||||
const levels = 20
|
||||
const CHUNK_TREE_HEIGHT = 2
|
||||
const CHUNK_TREE_HEIGHT = 8
|
||||
describe('Snark', () => {
|
||||
it('should work', async () => {
|
||||
const tree = new MerkleTree(levels, [], { hashFunction: poseidonHash2 })
|
||||
|
||||
@@ -17,7 +17,7 @@ async function register(note, tornadoTrees, from) {
|
||||
}
|
||||
|
||||
const levels = 20
|
||||
const CHUNK_TREE_HEIGHT = 2
|
||||
const CHUNK_TREE_HEIGHT = 8
|
||||
|
||||
const instances = [
|
||||
'0x1111000000000000000000000000000000001111',
|
||||
@@ -49,7 +49,6 @@ describe('TornadoTrees', function () {
|
||||
|
||||
const TornadoTreesV1 = await ethers.getContractFactory('TornadoTreesV1Mock')
|
||||
tornadoTreesV1 = await TornadoTreesV1.deploy(0, 0, tree.root(), tree.root())
|
||||
|
||||
notes = []
|
||||
for (let i = 0; i < 2 ** CHUNK_TREE_HEIGHT; i++) {
|
||||
notes[i] = {
|
||||
@@ -78,10 +77,10 @@ describe('TornadoTrees', function () {
|
||||
tornadoTreesV1.address,
|
||||
verifier.address,
|
||||
{
|
||||
unprocessedDeposits: 1,
|
||||
unprocessedWithdrawals: 1,
|
||||
depositsPerDay: 2,
|
||||
withdrawalsPerDay: 2,
|
||||
depositsFrom: 1,
|
||||
depositsStep: 1,
|
||||
withdrawalsFrom: 2,
|
||||
withdrawalsStep: 2,
|
||||
},
|
||||
)
|
||||
depositDataEventFilter = tornadoTrees.filters.DepositData()
|
||||
@@ -141,14 +140,15 @@ describe('TornadoTrees', function () {
|
||||
instance: toFixedHex(e.args.instance, 20),
|
||||
block: toFixedHex(e.args.block, 4),
|
||||
}))
|
||||
;({ input, args } = controller.batchTreeUpdate(tree, registeredEvents.slice(0, 4)))
|
||||
;({ input, args } = controller.batchTreeUpdate(tree, registeredEvents.slice(0, notes.length)))
|
||||
proof = await controller.prove(input, './artifacts/circuits/BatchTreeUpdate')
|
||||
await tornadoTrees.updateDepositTree(proof, ...args)
|
||||
updatedRoot = await tornadoTrees.depositRoot()
|
||||
expect(updatedRoot).to.be.equal(tree.root())
|
||||
})
|
||||
it('should work for batch+N filled v1 tree', async () => {
|
||||
for (let i = 4; i < 6; i++) {
|
||||
const batchSize = 2 ** CHUNK_TREE_HEIGHT
|
||||
for (let i = batchSize; i < batchSize + 2; i++) {
|
||||
notes.push({
|
||||
instance: instances[i % instances.length],
|
||||
depositBlock: blocks[i % blocks.length],
|
||||
@@ -166,34 +166,39 @@ describe('TornadoTrees', function () {
|
||||
tornadoTreesV1.address,
|
||||
verifier.address,
|
||||
{
|
||||
unprocessedDeposits: 1,
|
||||
unprocessedWithdrawals: 1,
|
||||
depositsPerDay: 2,
|
||||
withdrawalsPerDay: 2,
|
||||
depositsFrom: 1,
|
||||
depositsStep: 1,
|
||||
withdrawalsFrom: 2,
|
||||
withdrawalsStep: 2,
|
||||
},
|
||||
)
|
||||
|
||||
// load first batchSize deposits
|
||||
let { input, args } = controller.batchTreeUpdate(tree, depositEvents)
|
||||
let proof = await controller.prove(input, './artifacts/circuits/BatchTreeUpdate')
|
||||
await newTornadoTrees.updateDepositTree(proof, ...args)
|
||||
let updatedRoot = await newTornadoTrees.depositRoot()
|
||||
expect(updatedRoot).to.be.equal(tree.root())
|
||||
|
||||
// register 6 new deposits for the new trees
|
||||
// register 2 * `notes.length` new deposits on the new trees
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
await register(notes[i], newTornadoTrees, tornadoProxy)
|
||||
}
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
await register(notes[i], newTornadoTrees, tornadoProxy)
|
||||
}
|
||||
|
||||
// get 2 events from v1 tress
|
||||
let events = notes.slice(4).map((note) => ({
|
||||
// get 2 extra events from v1 tress
|
||||
let events = notes.slice(batchSize).map((note) => ({
|
||||
hash: toFixedHex(note.commitment),
|
||||
instance: toFixedHex(note.instance, 20),
|
||||
block: toFixedHex(note.depositBlock, 4),
|
||||
}))
|
||||
|
||||
const registeredEvents = await newTornadoTrees.queryFilter(depositDataEventFilter)
|
||||
let registeredEvents = await newTornadoTrees.queryFilter(depositDataEventFilter)
|
||||
registeredEvents = registeredEvents.slice(batchSize) // cut processed deposits from v1
|
||||
events = events.concat(
|
||||
registeredEvents.slice(0, 2).map((e) => ({
|
||||
registeredEvents.slice(0, batchSize - 2).map((e) => ({
|
||||
hash: toFixedHex(e.args.hash),
|
||||
instance: toFixedHex(e.args.instance, 20),
|
||||
block: toFixedHex(e.args.block, 4),
|
||||
@@ -206,7 +211,7 @@ describe('TornadoTrees', function () {
|
||||
updatedRoot = await newTornadoTrees.depositRoot()
|
||||
expect(updatedRoot).to.be.equal(tree.root())
|
||||
|
||||
events = registeredEvents.slice(6).map((e) => ({
|
||||
events = registeredEvents.slice(batchSize - 2, 2 * batchSize - 2).map((e) => ({
|
||||
hash: toFixedHex(e.args.hash),
|
||||
instance: toFixedHex(e.args.instance, 20),
|
||||
block: toFixedHex(e.args.block, 4),
|
||||
|
||||
Reference in New Issue
Block a user