findArrayLength WIP

This commit is contained in:
Alexey 2021-02-06 16:13:22 +03:00
parent 9039f38440
commit 8a6146d16d
6 changed files with 176 additions and 16 deletions

@ -62,11 +62,19 @@ contract TornadoTrees is EnsResolve {
_; _;
} }
struct SearchParams {
uint256 unprocessedDeposits;
uint256 unprocessedWithdrawals;
uint256 depositsPerDay;
uint256 withdrawalsPerDay;
}
constructor( constructor(
address _governance, address _governance,
address _tornadoProxy, address _tornadoProxy,
ITornadoTreesV1 _tornadoTreesV1, ITornadoTreesV1 _tornadoTreesV1,
IVerifier _treeUpdateVerifier IVerifier _treeUpdateVerifier,
SearchParams memory _searchParams
) public { ) public {
governance = _governance; governance = _governance;
tornadoProxy = _tornadoProxy; tornadoProxy = _tornadoProxy;
@ -79,30 +87,63 @@ contract TornadoTrees is EnsResolve {
uint256 depositLeaf = _tornadoTreesV1.lastProcessedDepositLeaf(); uint256 depositLeaf = _tornadoTreesV1.lastProcessedDepositLeaf();
require(depositLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state"); require(depositLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state");
lastProcessedDepositLeaf = depositLeaf; lastProcessedDepositLeaf = depositLeaf;
depositsLength = depositV1Length = findArrayLength(_tornadoTreesV1, "deposits(uint256)", 3); // todo depositsLength = depositV1Length = findArrayLength(
_tornadoTreesV1,
"deposits(uint256)",
_searchParams.unprocessedDeposits,
_searchParams.depositsPerDay
);
uint256 withdrawalLeaf = _tornadoTreesV1.lastProcessedWithdrawalLeaf(); uint256 withdrawalLeaf = _tornadoTreesV1.lastProcessedWithdrawalLeaf();
require(withdrawalLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state"); require(withdrawalLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state");
lastProcessedWithdrawalLeaf = withdrawalLeaf; lastProcessedWithdrawalLeaf = withdrawalLeaf;
withdrawalsLength = withdrawalsV1Length = findArrayLength(_tornadoTreesV1, "withdrawals(uint256)", 3); // todo withdrawalsLength = withdrawalsV1Length = findArrayLength(
_tornadoTreesV1,
"withdrawals(uint256)",
_searchParams.unprocessedWithdrawals,
_searchParams.withdrawalsPerDay
);
} }
// todo implement binary search
function findArrayLength( function findArrayLength(
ITornadoTreesV1 _tornadoTreesV1, ITornadoTreesV1 _tornadoTreesV1,
string memory _signature, string memory _type,
uint256 _from uint256 _from,
uint256 _step
) public view returns (uint256) { ) public view returns (uint256) {
bool success; require(_from != 0 && _step != 0, "_from and _step should be > 0");
bytes memory data; require(elementExists(_tornadoTreesV1, _type, _from), "Inccorrect _from param");
(success, data) = address(_tornadoTreesV1).staticcall{ gas: 3000 }(abi.encodeWithSignature(_signature, _from)); uint256 index = _from + _step;
while (success) { while (elementExists(_tornadoTreesV1, _type, index)) {
_from++; index = index + _step;
(success, data) = address(_tornadoTreesV1).staticcall{ gas: 3000 }(abi.encodeWithSignature(_signature, _from));
} }
return _from; uint256 high = index;
uint256 low = index - _step;
uint256 mid = (low + high) / 2;
while (!elementExists(_tornadoTreesV1, _type, mid)) {
high = mid - 1;
mid = (low + high) / 2;
}
high += 1;
low = mid + 1;
mid = (low + high) / 2;
while (elementExists(_tornadoTreesV1, _type, mid)) {
low = mid + 1;
mid = (low + high) / 2;
}
return high == low ? high : low;
}
function elementExists(
ITornadoTreesV1 _tornadoTreesV1,
string memory _type,
uint256 index
) public view returns (bool success) {
(success, ) = address(_tornadoTreesV1).staticcall{ gas: 2500 }(abi.encodeWithSignature(_type, index));
} }
function registerDeposit(address _instance, bytes32 _commitment) public onlyTornadoProxy { function registerDeposit(address _instance, bytes32 _commitment) public onlyTornadoProxy {

@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract PublicArray {
uint256[] public deposits;
uint256[] public withdrawals;
function lastProcessedDepositLeaf() external view returns (uint256) {}
function lastProcessedWithdrawalLeaf() external view returns (uint256) {}
function depositRoot() external view returns (bytes32) {}
function withdrawalRoot() external view returns (bytes32) {}
function setDeposits(uint256[] memory _deposits) public {
for (uint256 i = 0; i < _deposits.length; i++) {
deposits.push(_deposits[i]);
}
}
function setWithdrawals(uint256[] memory _withdrawals) public {
for (uint256 i = 0; i < _withdrawals.length; i++) {
withdrawals.push(_withdrawals[i]);
}
}
}

@ -14,8 +14,9 @@ contract TornadoTreesMock is TornadoTrees {
address _governance, address _governance,
address _tornadoProxy, address _tornadoProxy,
ITornadoTreesV1 _tornadoTreesV1, ITornadoTreesV1 _tornadoTreesV1,
IVerifier _treeUpdateVerifier IVerifier _treeUpdateVerifier,
) public TornadoTrees(_governance, _tornadoProxy, _tornadoTreesV1, _treeUpdateVerifier) {} SearchParams memory _searchParams
) public TornadoTrees(_governance, _tornadoProxy, _tornadoTreesV1, _treeUpdateVerifier, _searchParams) {}
function resolve(bytes32 _addr) public view override returns (address) { function resolve(bytes32 _addr) public view override returns (address) {
return address(uint160(uint256(_addr) >> (12 * 8))); return address(uint160(uint256(_addr) >> (12 * 8)));

@ -21,7 +21,7 @@ const config = {
solidity: '0.6.12', solidity: '0.6.12',
networks: { networks: {
hardhat: { hardhat: {
blockGasLimit: 950000000, blockGasLimit: 9500000,
}, },
}, },
mocha: { mocha: {

78
test/binarySearch.test.js Normal file

@ -0,0 +1,78 @@
/* global ethers */
const { expect } = require('chai')
const depositsEven = [10, 11, 12, 13, 14, 15, 16, 17, 18]
const depositsOdd = [10, 11, 12, 13, 14, 15, 16, 17]
describe('findArrayLength', () => {
let publicArray
let tornadoTrees
let PublicArray
beforeEach(async function () {
const [operator, tornadoProxy] = await ethers.getSigners()
PublicArray = await ethers.getContractFactory('PublicArray')
publicArray = await PublicArray.deploy()
await publicArray.setDeposits(depositsEven)
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,
},
)
})
it('should work for even array', async () => {
const depositsLength = await tornadoTrees.findArrayLength(publicArray.address, 'deposits(uint256)', 4, 2)
expect(depositsLength).to.be.equal(depositsEven.length)
})
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)
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)
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)
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)
expect(depositsLength).to.be.equal(depositsOdd.length)
})
it('should work for big array and big step', async () => {
const deposits = Array.from(Array(100).keys())
publicArray = await PublicArray.deploy()
await publicArray.setDeposits(deposits)
const depositsLength = await tornadoTrees.findArrayLength(
publicArray.address,
'deposits(uint256)',
67,
10,
)
expect(depositsLength).to.be.equal(deposits.length)
})
})

@ -77,6 +77,12 @@ describe('TornadoTrees', function () {
tornadoProxy.address, tornadoProxy.address,
tornadoTreesV1.address, tornadoTreesV1.address,
verifier.address, verifier.address,
{
unprocessedDeposits: 1,
unprocessedWithdrawals: 1,
depositsPerDay: 2,
withdrawalsPerDay: 2,
},
) )
depositDataEventFilter = tornadoTrees.filters.DepositData() depositDataEventFilter = tornadoTrees.filters.DepositData()
}) })
@ -159,6 +165,12 @@ describe('TornadoTrees', function () {
tornadoProxy.address, tornadoProxy.address,
tornadoTreesV1.address, tornadoTreesV1.address,
verifier.address, verifier.address,
{
unprocessedDeposits: 1,
unprocessedWithdrawals: 1,
depositsPerDay: 2,
withdrawalsPerDay: 2,
},
) )
let { input, args } = controller.batchTreeUpdate(tree, depositEvents) let { input, args } = controller.batchTreeUpdate(tree, depositEvents)