Compare commits
43 Commits
Author | SHA1 | Date |
---|---|---|
Tomoaki Imai | d95b53862f | |
Tomoaki Imai | f6bfbf09be | |
poma | 17fc574af3 | |
poma | a18cc18e91 | |
poma | 790f049719 | |
poma | 53e7a69556 | |
poma | 8a82afba42 | |
poma | a04fe966f7 | |
poma | 9321740be7 | |
poma | a3da216e03 | |
poma | 87ce87532e | |
poma | a8b93f6d5b | |
poma | 7d076e8af1 | |
poma | 0895afbc30 | |
poma | e8954ec6ce | |
poma | 73b9369abc | |
poma | 628d3bd64a | |
poma | b8c403b435 | |
Alexey | 44b3db0ce4 | |
poma | 5492acd75c | |
poma | 55937915c4 | |
poma | 272c7a94e0 | |
Alexey | 21711da0b6 | |
Alexey | 76543d068b | |
poma | b871d1e49f | |
poma | 6f497e495e | |
Alexey | bf5581f764 | |
Alexey | 0cabd318cd | |
Alexey | 2dd8c95300 | |
Alexey | 7abc834475 | |
Alexey | 4f732d05bc | |
poma | 93dd5d5e86 | |
poma | 06244af218 | |
poma | 2bcb37c628 | |
poma | 59f13c3aaa | |
poma | 2667feb4f1 | |
Alexey | a03efa767c | |
Alexey | 11c48fae14 | |
Alexey | fe2912fc33 | |
poma | 9fdafd2765 | |
Alexey | 6450153f2b | |
Alexey | 6f42be6213 | |
Alexey | 6a01ff9642 |
|
@ -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 . .
|
||||
|
|
16
README.md
16
README.md
|
@ -15,3 +15,19 @@ $ 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
|
||||
```
|
||||
|
||||
## build large circuits
|
||||
|
||||
Make sure you have enough RAM
|
||||
|
||||
```bash
|
||||
docker build . -t tornadocash/tornado-trees
|
||||
```
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
include "../node_modules/circomlib/circuits/poseidon.circom";
|
||||
include "../node_modules/circomlib/circuits/bitify.circom";
|
||||
include "./MerkleTreeUpdater.circom";
|
||||
include "./Utils.circom";
|
||||
include "./TreeUpdateArgsHasher.circom";
|
||||
|
||||
// Computes hashes of the next tree layer
|
||||
template TreeLayer(height) {
|
||||
signal input ins[1 << (height + 1)];
|
||||
signal output outs[1 << height];
|
||||
var nItems = 1 << height;
|
||||
signal input ins[nItems * 2];
|
||||
signal output outs[nItems];
|
||||
|
||||
component hash[1 << height];
|
||||
for(var i = 0; i < (1 << height); i++) {
|
||||
component hash[nItems];
|
||||
for(var i = 0; i < nItems; i++) {
|
||||
hash[i] = HashLeftRight();
|
||||
hash[i].left <== ins[i * 2];
|
||||
hash[i].right <== ins[i * 2 + 1];
|
||||
|
@ -18,6 +20,8 @@ template TreeLayer(height) {
|
|||
|
||||
// Inserts a leaf batch into a tree
|
||||
// Checks that tree previously contained zero leaves in the same position
|
||||
// Hashes leaves with Poseidon hash
|
||||
// `batchLevels` should be less than `levels`
|
||||
template BatchTreeUpdate(levels, batchLevels, zeroBatchLeaf) {
|
||||
var height = levels - batchLevels;
|
||||
var nLeaves = 1 << batchLevels;
|
||||
|
@ -74,20 +78,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))
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
include "../node_modules/circomlib/circuits/bitify.circom";
|
||||
include "../node_modules/circomlib/circuits/sha256/sha256.circom";
|
||||
|
||||
// Computes a SHA256 hash of all inputs packed into a byte array
|
||||
// Field elements are padded to 256 bits with zeroes
|
||||
template TreeUpdateArgsHasher(nLeaves) {
|
||||
signal input oldRoot;
|
||||
signal input newRoot;
|
||||
signal input pathIndices;
|
||||
signal input instances[nLeaves];
|
||||
signal input hashes[nLeaves];
|
||||
signal input blocks[nLeaves];
|
||||
signal output out;
|
||||
|
||||
var header = 256 + 256 + 32;
|
||||
var bitsPerLeaf = 256 + 160 + 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;
|
||||
|
||||
var index = 0;
|
||||
|
||||
hasher.in[index++] <== 0;
|
||||
hasher.in[index++] <== 0;
|
||||
for(var i = 0; i < 254; i++) {
|
||||
hasher.in[index++] <== bitsOldRoot.out[253 - i];
|
||||
}
|
||||
hasher.in[index++] <== 0;
|
||||
hasher.in[index++] <== 0;
|
||||
for(var i = 0; i < 254; i++) {
|
||||
hasher.in[index++] <== bitsNewRoot.out[253 - i];
|
||||
}
|
||||
for(var i = 0; i < 32; i++) {
|
||||
hasher.in[index++] <== 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[index++] <== 0;
|
||||
hasher.in[index++] <== 0;
|
||||
for(var i = 0; i < 254; i++) {
|
||||
hasher.in[index++] <== bitsHash[leaf].out[253 - i];
|
||||
}
|
||||
for(var i = 0; i < 160; i++) {
|
||||
hasher.in[index++] <== bitsInstance[leaf].out[159 - i];
|
||||
}
|
||||
for(var i = 0; i < 32; i++) {
|
||||
hasher.in[index++] <== 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;
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
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);
|
||||
|
||||
component bitsOldRoot = Num2Bits(256);
|
||||
component bitsNewRoot = Num2Bits(256);
|
||||
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];
|
||||
}
|
||||
for(var i = 0; i < 256; i++) {
|
||||
hasher.in[i + 256] <== bitsNewRoot.out[255 - 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);
|
||||
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];
|
||||
}
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// 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,
|
||||
address _admin,
|
||||
bytes memory _data
|
||||
) public payable TransparentUpgradeableProxy(_logic, _admin, _data) {}
|
||||
|
||||
/**
|
||||
* @dev Override to allow admin access the fallback function.
|
||||
*/
|
||||
function _beforeFallback() internal override {}
|
||||
}
|
|
@ -5,8 +5,10 @@ pragma experimental ABIEncoderV2;
|
|||
|
||||
import "./interfaces/ITornadoTreesV1.sol";
|
||||
import "./interfaces/IBatchTreeUpdateVerifier.sol";
|
||||
import "@openzeppelin/upgrades-core/contracts/Initializable.sol";
|
||||
|
||||
contract TornadoTrees {
|
||||
/// @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;
|
||||
|
@ -16,8 +18,7 @@ contract TornadoTrees {
|
|||
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 +27,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;
|
||||
|
@ -35,6 +36,8 @@ contract TornadoTrees {
|
|||
|
||||
event DepositData(address instance, bytes32 indexed hash, uint256 block, uint256 index);
|
||||
event WithdrawalData(address instance, bytes32 indexed hash, uint256 block, uint256 index);
|
||||
event VerifierUpdated(address newVerifier);
|
||||
event ProxyUpdated(address newProxy);
|
||||
|
||||
struct TreeLeaf {
|
||||
bytes32 hash;
|
||||
|
@ -42,13 +45,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,56 +56,186 @@ 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,
|
||||
IBatchTreeUpdateVerifier _treeUpdateVerifier,
|
||||
SearchParams memory _searchParams
|
||||
) public {
|
||||
governance = _governance;
|
||||
tornadoProxy = _tornadoProxy;
|
||||
treeUpdateVerifier = _treeUpdateVerifier;
|
||||
tornadoTreesV1 = _tornadoTreesV1;
|
||||
|
||||
depositRoot = _tornadoTreesV1.depositRoot();
|
||||
uint256 lastDepositLeaf = _tornadoTreesV1.lastProcessedDepositLeaf();
|
||||
require(lastDepositLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state");
|
||||
lastProcessedDepositLeaf = lastDepositLeaf;
|
||||
depositsLength = depositV1Length = findArrayLength(
|
||||
depositsV1Length = findArrayLength(
|
||||
_tornadoTreesV1,
|
||||
"deposits(uint256)",
|
||||
_searchParams.unprocessedDeposits,
|
||||
_searchParams.depositsPerDay
|
||||
_searchParams.depositsFrom,
|
||||
_searchParams.depositsStep
|
||||
);
|
||||
|
||||
withdrawalRoot = _tornadoTreesV1.withdrawalRoot();
|
||||
uint256 lastWithdrawalLeaf = _tornadoTreesV1.lastProcessedWithdrawalLeaf();
|
||||
require(lastWithdrawalLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state");
|
||||
lastProcessedWithdrawalLeaf = lastWithdrawalLeaf;
|
||||
withdrawalsLength = withdrawalsV1Length = findArrayLength(
|
||||
withdrawalsV1Length = findArrayLength(
|
||||
_tornadoTreesV1,
|
||||
"withdrawals(uint256)",
|
||||
_searchParams.unprocessedWithdrawals,
|
||||
_searchParams.withdrawalsPerDay
|
||||
_searchParams.withdrawalsFrom,
|
||||
_searchParams.withdrawalsStep
|
||||
);
|
||||
}
|
||||
|
||||
// todo make things internal
|
||||
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;
|
||||
}
|
||||
|
||||
/// @dev Queue a new deposit data to be inserted into a merkle tree
|
||||
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;
|
||||
}
|
||||
|
||||
/// @dev Queue a new withdrawal data to be inserted into a merkle tree
|
||||
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;
|
||||
}
|
||||
|
||||
/// @dev Insert a full batch of queued deposits into a merkle tree
|
||||
/// @param _proof A snark proof that elements were inserted correctly
|
||||
/// @param _argsHash A hash of snark inputs
|
||||
/// @param _currentRoot Current merkle tree root
|
||||
/// @param _newRoot Updated merkle tree root
|
||||
/// @param _pathIndices Merkle path to inserted batch
|
||||
/// @param _events A batch of inserted events (leaves)
|
||||
function updateDepositTree(
|
||||
bytes calldata _proof,
|
||||
bytes32 _argsHash,
|
||||
bytes32 _currentRoot,
|
||||
bytes32 _newRoot,
|
||||
uint32 _pathIndices,
|
||||
TreeLeaf[CHUNK_SIZE] calldata _events
|
||||
) public {
|
||||
uint256 offset = lastProcessedDepositLeaf;
|
||||
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 {
|
||||
let itemOffset := add(data, mul(ITEM_SIZE, i))
|
||||
mstore(add(itemOffset, 0x7c), blockNumber)
|
||||
mstore(add(itemOffset, 0x78), instance)
|
||||
mstore(add(itemOffset, 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;
|
||||
}
|
||||
|
||||
/// @dev Insert a full batch of queued withdrawals into a merkle tree
|
||||
/// @param _proof A snark proof that elements were inserted correctly
|
||||
/// @param _argsHash A hash of snark inputs
|
||||
/// @param _currentRoot Current merkle tree root
|
||||
/// @param _newRoot Updated merkle tree root
|
||||
/// @param _pathIndices Merkle path to inserted batch
|
||||
/// @param _events A batch of inserted events (leaves)
|
||||
function updateWithdrawalTree(
|
||||
bytes calldata _proof,
|
||||
bytes32 _argsHash,
|
||||
bytes32 _currentRoot,
|
||||
bytes32 _newRoot,
|
||||
uint32 _pathIndices,
|
||||
TreeLeaf[CHUNK_SIZE] calldata _events
|
||||
) public {
|
||||
uint256 offset = lastProcessedWithdrawalLeaf;
|
||||
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 {
|
||||
let itemOffset := add(data, mul(ITEM_SIZE, i))
|
||||
mstore(add(itemOffset, 0x7c), blockNumber)
|
||||
mstore(add(itemOffset, 0x78), instance)
|
||||
mstore(add(itemOffset, 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 previous contract, so we have to find them length manually
|
||||
/// 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
|
||||
) public view returns (uint256) {
|
||||
) internal view virtual returns (uint256) {
|
||||
// Find the segment with correct array length
|
||||
bool direction = elementExists(_tornadoTreesV1, _type, _from);
|
||||
do {
|
||||
|
@ -140,139 +266,14 @@ contract TornadoTrees {
|
|||
(success, ) = address(_tornadoTreesV1).staticcall{ gas: 2500 }(abi.encodeWithSignature(_type, index));
|
||||
}
|
||||
|
||||
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 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 >= depositV1Length ? 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) {
|
||||
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,
|
||||
uint256 _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 insert index");
|
||||
require(uint256(_newRoot) < SNARK_FIELD, "Proposed root is out of range");
|
||||
|
||||
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");
|
||||
require(uint256(hash) < SNARK_FIELD, "Hash out of range");
|
||||
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");
|
||||
}
|
||||
|
||||
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;
|
||||
emit ProxyUpdated(_tornadoProxy);
|
||||
}
|
||||
|
||||
function setVerifierContract(IBatchTreeUpdateVerifier _treeUpdateVerifier) external onlyGovernance {
|
||||
treeUpdateVerifier = _treeUpdateVerifier;
|
||||
emit VerifierUpdated(address(_treeUpdateVerifier));
|
||||
}
|
||||
|
||||
function blockNumber() public view virtual returns (uint256) {
|
||||
|
|
|
@ -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;
|
||||
|
@ -24,11 +24,11 @@ contract Pack {
|
|||
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]);
|
||||
(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)
|
||||
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();
|
||||
|
@ -52,23 +52,23 @@ contract Pack {
|
|||
public
|
||||
view
|
||||
returns (
|
||||
uint256 gas1,
|
||||
uint256 gas2,
|
||||
bytes32 hash
|
||||
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]);
|
||||
(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)
|
||||
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 hash = sha256(data);
|
||||
return (gasleft() - gasHash, gasHash - gasBefore, hash);
|
||||
bytes32 hash1 = sha256(data);
|
||||
return (gasleft() - gasHash, gasHash - gasBefore, hash1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,11 +12,9 @@ contract TornadoTreesMock is TornadoTrees {
|
|||
|
||||
constructor(
|
||||
address _governance,
|
||||
address _tornadoProxy,
|
||||
ITornadoTreesV1 _tornadoTreesV1,
|
||||
IBatchTreeUpdateVerifier _treeUpdateVerifier,
|
||||
SearchParams memory _searchParams
|
||||
) public TornadoTrees(_governance, _tornadoProxy, _tornadoTreesV1, _treeUpdateVerifier, _searchParams) {}
|
||||
) public TornadoTrees(_governance, _tornadoTreesV1, _searchParams) {}
|
||||
|
||||
function setBlockNumber(uint256 _blockNumber) public {
|
||||
currentBlock = _blockNumber;
|
||||
|
@ -26,6 +24,15 @@ contract TornadoTreesMock is TornadoTrees {
|
|||
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,
|
||||
|
@ -40,6 +47,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,
|
||||
|
@ -85,4 +97,32 @@ contract TornadoTreesMock is TornadoTrees {
|
|||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
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 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 override returns (uint256) {
|
||||
if (_from == 0 && _step == 0) {
|
||||
return 0;
|
||||
}
|
||||
return super.findArrayLength(_tornadoTreesV1, _type, _from, _step);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
11
package.json
11
package.json
|
@ -1,13 +1,14 @@
|
|||
{
|
||||
"name": "tornado-trees",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.11",
|
||||
"main": "src/index.js",
|
||||
"repository": "https://github.com/tornadocash/tornado-trees.git",
|
||||
"author": "Tornadocash team <hello@tornado.cash>",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"src/*",
|
||||
"contracts/*"
|
||||
"contracts/*",
|
||||
"scripts/*"
|
||||
],
|
||||
"scripts": {
|
||||
"compile": "npx hardhat compile",
|
||||
|
@ -16,6 +17,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",
|
||||
|
@ -38,11 +40,14 @@
|
|||
"torn-token": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"circom": "^0.5.38",
|
||||
"@openzeppelin/contracts": "^3.4.0",
|
||||
"@openzeppelin/upgrades-core": "^1.5.1",
|
||||
"circom": "0.5.42",
|
||||
"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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) =>
|
||||
|
|
|
@ -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)
|
||||
})
|
30
src/index.js
30
src/index.js
|
@ -1,5 +1,7 @@
|
|||
const ethers = require('ethers')
|
||||
const BigNumber = ethers.BigNumber
|
||||
const { wtns } = require('snarkjs')
|
||||
const { utils } = require('ffjavascript')
|
||||
|
||||
const { bitsToNumber, toBuffer, toFixedHex, poseidonHash } = require('./utils')
|
||||
|
||||
|
@ -32,16 +34,24 @@ function hashInputs(input) {
|
|||
function prove(input, keyBasePath) {
|
||||
return tmp.dir().then(async (dir) => {
|
||||
dir = dir.path
|
||||
fs.writeFileSync(`${dir}/input.json`, JSON.stringify(input, null, 2))
|
||||
let out
|
||||
|
||||
try {
|
||||
if (fs.existsSync(`${keyBasePath}`)) {
|
||||
// native witness calc
|
||||
fs.writeFileSync(`${dir}/input.json`, JSON.stringify(input, null, 2))
|
||||
out = await exec(`${keyBasePath} ${dir}/input.json ${dir}/witness.json`)
|
||||
} else {
|
||||
out = await exec(`npx snarkjs wd ${keyBasePath}.wasm ${dir}/input.json ${dir}/witness.wtns`)
|
||||
out = await exec(`npx snarkjs wej ${dir}/witness.wtns ${dir}/witness.json`)
|
||||
await wtns.debug(
|
||||
utils.unstringifyBigInts(input),
|
||||
`${keyBasePath}.wasm`,
|
||||
`${dir}/witness.wtns`,
|
||||
`${keyBasePath}.sym`,
|
||||
{},
|
||||
console,
|
||||
)
|
||||
const witness = utils.stringifyBigInts(await wtns.exportJson(`${dir}/witness.wtns`))
|
||||
fs.writeFileSync(`${dir}/witness.json`, JSON.stringify(witness, null, 2))
|
||||
}
|
||||
out = await exec(
|
||||
`zkutil prove -c ${keyBasePath}.r1cs -p ${keyBasePath}.params -w ${dir}/witness.json -r ${dir}/proof.json -o ${dir}/public.json`,
|
||||
|
@ -54,10 +64,18 @@ function prove(input, keyBasePath) {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates inputs for a snark and tornado trees smart contract.
|
||||
* This function updates MerkleTree argument
|
||||
*
|
||||
* @param tree Merkle tree with current smart contract state. This object is mutated during function execution.
|
||||
* @param events New batch of events to insert.
|
||||
* @returns {{args: [string, string, string, string, *], input: {pathElements: *, instances: *, blocks: *, newRoot: *, hashes: *, oldRoot: *, pathIndices: string}}}
|
||||
*/
|
||||
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()
|
||||
|
|
|
@ -18,48 +18,80 @@ describe('findArrayLength', () => {
|
|||
await publicArray.setWithdrawals(depositsEven)
|
||||
|
||||
const TornadoTrees = await ethers.getContractFactory('TornadoTreesMock')
|
||||
tornadoTrees = await TornadoTrees.deploy(
|
||||
operator.address,
|
||||
tornadoProxy.address,
|
||||
publicArray.address,
|
||||
publicArray.address,
|
||||
{
|
||||
unprocessedDeposits: 3,
|
||||
unprocessedWithdrawals: 3,
|
||||
depositsPerDay: 2,
|
||||
withdrawalsPerDay: 2,
|
||||
},
|
||||
)
|
||||
tornadoTrees = await TornadoTrees.deploy(operator.address, publicArray.address, {
|
||||
depositsFrom: 3,
|
||||
depositsStep: 3,
|
||||
withdrawalsFrom: 2,
|
||||
withdrawalsStep: 2,
|
||||
})
|
||||
await tornadoTrees.initialize(tornadoProxy.address, publicArray.address)
|
||||
})
|
||||
|
||||
it('should work for even array', async () => {
|
||||
const depositsLength = await tornadoTrees.findArrayLength(publicArray.address, 'deposits(uint256)', 4, 2)
|
||||
const depositsLength = await tornadoTrees.findArrayLengthMock(
|
||||
publicArray.address,
|
||||
'deposits(uint256)',
|
||||
4,
|
||||
2,
|
||||
)
|
||||
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.findArrayLengthMock(
|
||||
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)
|
||||
const depositsLength = await tornadoTrees.findArrayLength(publicArray.address, 'deposits(uint256)', 4, 2)
|
||||
const depositsLength = await tornadoTrees.findArrayLengthMock(
|
||||
publicArray.address,
|
||||
'deposits(uint256)',
|
||||
4,
|
||||
2,
|
||||
)
|
||||
expect(depositsLength).to.be.equal(depositsOdd.length)
|
||||
})
|
||||
|
||||
it('should work for even array and odd step', async () => {
|
||||
const depositsLength = await tornadoTrees.findArrayLength(publicArray.address, 'deposits(uint256)', 4, 3)
|
||||
const depositsLength = await tornadoTrees.findArrayLengthMock(
|
||||
publicArray.address,
|
||||
'deposits(uint256)',
|
||||
4,
|
||||
3,
|
||||
)
|
||||
expect(depositsLength).to.be.equal(depositsEven.length)
|
||||
})
|
||||
|
||||
it('should work for odd array and odd step', async () => {
|
||||
publicArray = await PublicArray.deploy()
|
||||
await publicArray.setDeposits(depositsOdd)
|
||||
const depositsLength = await tornadoTrees.findArrayLength(publicArray.address, 'deposits(uint256)', 4, 3)
|
||||
const depositsLength = await tornadoTrees.findArrayLengthMock(
|
||||
publicArray.address,
|
||||
'deposits(uint256)',
|
||||
4,
|
||||
3,
|
||||
)
|
||||
expect(depositsLength).to.be.equal(depositsOdd.length)
|
||||
})
|
||||
|
||||
it('should work for odd array and step 1', async () => {
|
||||
publicArray = await PublicArray.deploy()
|
||||
await publicArray.setDeposits(depositsOdd)
|
||||
const depositsLength = await tornadoTrees.findArrayLength(publicArray.address, 'deposits(uint256)', 4, 1)
|
||||
const depositsLength = await tornadoTrees.findArrayLengthMock(
|
||||
publicArray.address,
|
||||
'deposits(uint256)',
|
||||
4,
|
||||
1,
|
||||
)
|
||||
expect(depositsLength).to.be.equal(depositsOdd.length)
|
||||
})
|
||||
|
||||
|
@ -67,7 +99,7 @@ describe('findArrayLength', () => {
|
|||
const deposits = Array.from(Array(100).keys())
|
||||
publicArray = await PublicArray.deploy()
|
||||
await publicArray.setDeposits(deposits)
|
||||
const depositsLength = await tornadoTrees.findArrayLength(
|
||||
const depositsLength = await tornadoTrees.findArrayLengthMock(
|
||||
publicArray.address,
|
||||
'deposits(uint256)',
|
||||
67,
|
||||
|
@ -80,7 +112,12 @@ describe('findArrayLength', () => {
|
|||
const deposits = Array.from(Array(30).keys())
|
||||
publicArray = await PublicArray.deploy()
|
||||
await publicArray.setDeposits(deposits)
|
||||
const depositsLength = await tornadoTrees.findArrayLength(publicArray.address, 'deposits(uint256)', 1, 50)
|
||||
const depositsLength = await tornadoTrees.findArrayLengthMock(
|
||||
publicArray.address,
|
||||
'deposits(uint256)',
|
||||
1,
|
||||
50,
|
||||
)
|
||||
expect(depositsLength).to.be.equal(deposits.length)
|
||||
})
|
||||
|
||||
|
@ -98,7 +135,7 @@ describe('findArrayLength', () => {
|
|||
const deposits = Array.from(Array(len).keys())
|
||||
publicArray = await PublicArray.deploy()
|
||||
await publicArray.setDeposits(deposits)
|
||||
const depositsLength = await tornadoTrees.findArrayLength(
|
||||
const depositsLength = await tornadoTrees.findArrayLengthMock(
|
||||
publicArray.address,
|
||||
'deposits(uint256)',
|
||||
days * depositsPerDay,
|
||||
|
|
|
@ -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] = {
|
||||
|
@ -72,18 +71,13 @@ describe('TornadoTrees', function () {
|
|||
}
|
||||
}
|
||||
const TornadoTrees = await ethers.getContractFactory('TornadoTreesMock')
|
||||
tornadoTrees = await TornadoTrees.deploy(
|
||||
operator.address,
|
||||
tornadoProxy.address,
|
||||
tornadoTreesV1.address,
|
||||
verifier.address,
|
||||
{
|
||||
unprocessedDeposits: 1,
|
||||
unprocessedWithdrawals: 1,
|
||||
depositsPerDay: 2,
|
||||
withdrawalsPerDay: 2,
|
||||
},
|
||||
)
|
||||
tornadoTrees = await TornadoTrees.deploy(operator.address, tornadoTreesV1.address, {
|
||||
depositsFrom: 1,
|
||||
depositsStep: 1,
|
||||
withdrawalsFrom: 2,
|
||||
withdrawalsStep: 2,
|
||||
})
|
||||
await tornadoTrees.initialize(tornadoProxy.address, verifier.address)
|
||||
depositDataEventFilter = tornadoTrees.filters.DepositData()
|
||||
})
|
||||
|
||||
|
@ -141,14 +135,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],
|
||||
|
@ -160,40 +155,40 @@ describe('TornadoTrees', function () {
|
|||
}
|
||||
|
||||
const TornadoTrees = await ethers.getContractFactory('TornadoTreesMock')
|
||||
const newTornadoTrees = await TornadoTrees.deploy(
|
||||
operator.address,
|
||||
tornadoProxy.address,
|
||||
tornadoTreesV1.address,
|
||||
verifier.address,
|
||||
{
|
||||
unprocessedDeposits: 1,
|
||||
unprocessedWithdrawals: 1,
|
||||
depositsPerDay: 2,
|
||||
withdrawalsPerDay: 2,
|
||||
},
|
||||
)
|
||||
const newTornadoTrees = await TornadoTrees.deploy(operator.address, tornadoTreesV1.address, {
|
||||
depositsFrom: 1,
|
||||
depositsStep: 1,
|
||||
withdrawalsFrom: 2,
|
||||
withdrawalsStep: 2,
|
||||
})
|
||||
await newTornadoTrees.initialize(tornadoProxy.address, verifier.address)
|
||||
|
||||
// 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 +201,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),
|
||||
|
|
96
yarn.lock
96
yarn.lock
|
@ -593,6 +593,27 @@
|
|||
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.3.0.tgz#ffdb693c5c349fc33bba420248dd3ac0a2d7c408"
|
||||
integrity sha512-AemZEsQYtUp1WRkcmZm1div5ORfTpLquLaziCIrSagjxyKdmObxuaY1yjQ5SHFMctR8rLwp706NXTbiIRJg7pw==
|
||||
|
||||
"@openzeppelin/contracts@^3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.0.tgz#9a1669ad5f9fdfb6e273bb5a4fed10cb4cc35eb0"
|
||||
integrity sha512-qh+EiHWzfY/9CORr+eRUkeEUP1WiFUcq3974bLHwyYzLBUtK6HPaMkIUHi74S1rDTZ0sNz42DwPc5A4IJvN3rg==
|
||||
|
||||
"@openzeppelin/upgrades-core@^1.5.1":
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.5.1.tgz#2165f0c2961cc748e7d1c2bce1ba96589d31390b"
|
||||
integrity sha512-rL0h/+Yfcky98XaxLRcxKunmC2uVP+dr9tVxzZfbjDpIco7VkmyODsI1YBPZTn3e5kCj7A6cmgNrFmao/UkQyA==
|
||||
dependencies:
|
||||
bn.js "^5.1.2"
|
||||
cbor "^7.0.0"
|
||||
chalk "^4.1.0"
|
||||
compare-versions "^3.6.0"
|
||||
debug "^4.1.1"
|
||||
ethereumjs-util "^7.0.3"
|
||||
fp-ts "^2.7.1"
|
||||
io-ts "^2.2.9"
|
||||
proper-lockfile "^4.1.1"
|
||||
solidity-ast "^0.4.15"
|
||||
|
||||
"@resolver-engine/core@^0.3.3":
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@resolver-engine/core/-/core-0.3.3.tgz#590f77d85d45bc7ecc4e06c654f41345db6ca967"
|
||||
|
@ -2066,6 +2087,13 @@ caseless@~0.12.0:
|
|||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
|
||||
|
||||
cbor@^7.0.0:
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.2.tgz#9f0b74f3dfc5896775e0802e7d68f9b6af530ce3"
|
||||
integrity sha512-YR6TF7LBhTqdz0vjtoY5lDnOhHXg8/mdHd2qZYQz5q8Pl7i56/ndiIGLkms1RpkFAqrT9IHGO3cjo58SfFsF2A==
|
||||
dependencies:
|
||||
nofilter "^2.0.0"
|
||||
|
||||
chai@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
|
||||
|
@ -2098,7 +2126,7 @@ chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2:
|
|||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^4.0.0:
|
||||
chalk@^4.0.0, chalk@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
|
||||
integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
|
||||
|
@ -2213,10 +2241,10 @@ circom@0.5.33:
|
|||
tmp-promise "^2.0.2"
|
||||
wasmbuilder "0.0.10"
|
||||
|
||||
circom@^0.5.38:
|
||||
version "0.5.38"
|
||||
resolved "https://registry.yarnpkg.com/circom/-/circom-0.5.38.tgz#c099fb196085837575fb266f37b0516b1ec56eb5"
|
||||
integrity sha512-PFlXto8gDysUlwk6z/GYbn1Mv5BtW9BI4769N9gSP0/7KDNSqLNyVmL4DgMLc67/EpG4qJLGch3SdgzQD+/cfw==
|
||||
circom@0.5.42:
|
||||
version "0.5.42"
|
||||
resolved "https://registry.yarnpkg.com/circom/-/circom-0.5.42.tgz#96a456f9538f4425654df091d15e3158e9da2acc"
|
||||
integrity sha512-v6+f9g3z2ia17NQvQmyZjvh8cE8O3GtxRE36KfJfx/a+s58Y7aEDWsUG+GFRJhp1ajiQELdj3NehY9vHSf5Rkg==
|
||||
dependencies:
|
||||
chai "^4.2.0"
|
||||
circom_runtime "0.1.12"
|
||||
|
@ -2401,6 +2429,11 @@ commander@3.0.2:
|
|||
resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
|
||||
integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==
|
||||
|
||||
compare-versions@^3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62"
|
||||
integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==
|
||||
|
||||
component-emitter@^1.2.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
||||
|
@ -3857,6 +3890,15 @@ ffjavascript@0.2.34, ffjavascript@^0.2.30:
|
|||
wasmcurves "0.0.14"
|
||||
worker-threads "^1.0.0"
|
||||
|
||||
ffjavascript@^0.2.35:
|
||||
version "0.2.35"
|
||||
resolved "https://registry.yarnpkg.com/ffjavascript/-/ffjavascript-0.2.35.tgz#9166d95173b1c0a743b455bb03a72b581922a42e"
|
||||
integrity sha512-xnC51tWbi0ah4SH+02jEfJyO+P+NiZWnxQrLDLtBYY1Dv3QM5ydxzd+gxnLEfWdT8i1bMM5pIh5P25l6fNCaVQ==
|
||||
dependencies:
|
||||
big-integer "^1.6.48"
|
||||
wasmcurves "0.0.14"
|
||||
web-worker "^1.0.0"
|
||||
|
||||
ffwasm@0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/ffwasm/-/ffwasm-0.0.7.tgz#23bb9a3537ecc87c0f24fcfb3a9ddd0e86855fff"
|
||||
|
@ -4091,6 +4133,11 @@ fp-ts@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a"
|
||||
integrity sha512-wDNqTimnzs8QqpldiId9OavWK2NptormjXnRJTQecNjzwfyp6P/8s/zG8e4h3ja3oqkKaY72UlTjQYt/1yXf9A==
|
||||
|
||||
fp-ts@^2.7.1:
|
||||
version "2.9.5"
|
||||
resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.9.5.tgz#6690cd8b76b84214a38fc77cbbbd04a38f86ea90"
|
||||
integrity sha512-MiHrA5teO6t8zKArE3DdMPT/Db6v2GUt5yfWnhBTrrsVfeCJUUnV6sgFvjGNBKDmEMqVwRFkEePL7wPwqrLKKA==
|
||||
|
||||
fragment-cache@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
|
||||
|
@ -4354,6 +4401,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
|
|||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
||||
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
|
||||
|
||||
graceful-fs@^4.2.4:
|
||||
version "4.2.6"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
|
||||
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
|
||||
|
||||
growl@1.10.5:
|
||||
version "1.10.5"
|
||||
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
|
||||
|
@ -4720,6 +4772,11 @@ io-ts@1.10.4:
|
|||
dependencies:
|
||||
fp-ts "^1.0.0"
|
||||
|
||||
io-ts@^2.2.9:
|
||||
version "2.2.15"
|
||||
resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-2.2.15.tgz#0b0b19a6f8a64f4a524ad5810d56432789428128"
|
||||
integrity sha512-ww2ZPrErx5pjCCI/tWRwjlEIDEndnN9kBIxAylXj+WNIH4ZVgaUqFuabGouehkRuvrmvzO5OnZmLf+o50h4izQ==
|
||||
|
||||
ipaddr.js@1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
|
||||
|
@ -6033,6 +6090,11 @@ node-gyp-build@^4.2.0:
|
|||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
|
||||
integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==
|
||||
|
||||
nofilter@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-2.0.0.tgz#57a2d7c6fcd34dd396f490d6942c4f58640b5823"
|
||||
integrity sha512-i3ck2PUWBa+trsGGBvwS3msnTowbFei5G++BgpOpT7y7VTrprXphMQP5svTdsMLdttKDZFo+5RqVWRqhmf+BwQ==
|
||||
|
||||
normalize-package-data@^2.3.2:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
||||
|
@ -6590,6 +6652,15 @@ promise-to-callback@^1.0.0:
|
|||
is-fn "^1.0.0"
|
||||
set-immediate-shim "^1.0.1"
|
||||
|
||||
proper-lockfile@^4.1.1:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f"
|
||||
integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.4"
|
||||
retry "^0.12.0"
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
proxy-addr@~2.0.5:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
|
||||
|
@ -7043,6 +7114,11 @@ ret@~0.1.10:
|
|||
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
||||
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
|
||||
|
||||
retry@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
|
||||
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
|
||||
|
||||
rimraf@2.6.3:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
||||
|
@ -7440,6 +7516,11 @@ solhint-plugin-prettier@^0.0.5:
|
|||
dependencies:
|
||||
prettier-linter-helpers "^1.0.0"
|
||||
|
||||
solidity-ast@^0.4.15:
|
||||
version "0.4.17"
|
||||
resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.17.tgz#e857b4f64466f3eb94da78fe961c0d256c84b228"
|
||||
integrity sha512-5jkkabmjPdy9W9c2DMQBlKobVBz7KDnipxn+0zW191uD6BML3w7dQ+ihUvwA9XOm9ILDECrb5Y8z2vu47STqCg==
|
||||
|
||||
solidity-comments-extractor@^0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.4.tgz#ce420aef23641ffd0131c7d80ba85b6e1e42147e"
|
||||
|
@ -8280,6 +8361,11 @@ wasmcurves@0.0.5:
|
|||
big-integer "^1.6.42"
|
||||
blakejs "^1.1.0"
|
||||
|
||||
web-worker@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.0.0.tgz#c7ced4e1eb6227636ada35056a9e5a477414e4d0"
|
||||
integrity sha512-BzuMqeKVkKKwHV6tJuwePFcxYMxvC97D448mXTgh/CxXAB4sRtoV26gRPN+JDxsXRR7QZyioMV9O6NzQaASf7Q==
|
||||
|
||||
web3-bzz@1.2.11:
|
||||
version "1.2.11"
|
||||
resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.11.tgz#41bc19a77444bd5365744596d778b811880f707f"
|
||||
|
|
Loading…
Reference in New Issue