From 27a00bfd5f12994c31b0da18ea19bf6d08569d6c Mon Sep 17 00:00:00 2001 From: poma Date: Fri, 1 Nov 2019 04:12:32 +0300 Subject: [PATCH 01/10] rename withdraw event --- contracts/Mixer.sol | 4 ++-- test/ERC20Mixer.test.js | 6 +++--- test/ETHMixer.test.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/Mixer.sol b/contracts/Mixer.sol index 1fd9466..223c3e0 100644 --- a/contracts/Mixer.sol +++ b/contracts/Mixer.sol @@ -37,7 +37,7 @@ contract Mixer is MerkleTreeWithHistory { } event Deposit(uint256 indexed commitment, uint256 leafIndex, uint256 timestamp); - event Withdraw(address to, uint256 nullifierHash, address indexed relayer, uint256 fee); + event Withdrawal(address to, uint256 nullifierHash, address indexed relayer, uint256 fee); /** @dev The constructor @@ -98,7 +98,7 @@ contract Mixer is MerkleTreeWithHistory { require(verifier.verifyProof(proof, input), "Invalid withdraw proof"); nullifierHashes[nullifierHash] = true; _processWithdraw(receiver, relayer, fee, refund); - emit Withdraw(receiver, nullifierHash, relayer, fee); + emit Withdrawal(receiver, nullifierHash, relayer, fee); } /** @dev this function is defined in a child contract */ diff --git a/test/ERC20Mixer.test.js b/test/ERC20Mixer.test.js index 8818a79..534a062 100644 --- a/test/ERC20Mixer.test.js +++ b/test/ERC20Mixer.test.js @@ -180,7 +180,7 @@ contract('ERC20Mixer', accounts => { ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(refund))) ethBalanceRelayerAfter.should.be.eq.BN(toBN(ethBalanceRelayerBefore).sub(toBN(refund))) - logs[0].event.should.be.equal('Withdraw') + logs[0].event.should.be.equal('Withdrawal') logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString())) logs[0].args.relayer.should.be.eq.BN(relayer) logs[0].args.fee.should.be.eq.BN(feeBN) @@ -304,7 +304,7 @@ contract('ERC20Mixer', accounts => { ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(refund)).sub(feeBN)) - logs[0].event.should.be.equal('Withdraw') + logs[0].event.should.be.equal('Withdrawal') logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString())) logs[0].args.relayer.should.be.eq.BN(operator) logs[0].args.fee.should.be.eq.BN(feeBN) @@ -386,7 +386,7 @@ contract('ERC20Mixer', accounts => { ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(refund)).sub(feeBN)) - logs[0].event.should.be.equal('Withdraw') + logs[0].event.should.be.equal('Withdrawal') logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString())) logs[0].args.relayer.should.be.eq.BN(operator) logs[0].args.fee.should.be.eq.BN(feeBN) diff --git a/test/ETHMixer.test.js b/test/ETHMixer.test.js index b1c00f2..aa8f3a6 100644 --- a/test/ETHMixer.test.js +++ b/test/ETHMixer.test.js @@ -242,7 +242,7 @@ contract('ETHMixer', accounts => { balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(value)).sub(feeBN)) - logs[0].event.should.be.equal('Withdraw') + logs[0].event.should.be.equal('Withdrawal') logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString())) logs[0].args.relayer.should.be.eq.BN(operator) logs[0].args.fee.should.be.eq.BN(feeBN) From 1fdabcc97c173d1890487a5f216bb82e81ef9ca2 Mon Sep 17 00:00:00 2001 From: poma Date: Sat, 2 Nov 2019 15:35:22 +0300 Subject: [PATCH 02/10] changed emptyElement to constant --- .env.example | 1 - cli.js | 8 +++----- contracts/ERC20Mixer.sol | 3 +-- contracts/ETHMixer.sol | 3 +-- contracts/MerkleTreeWithHistory.sol | 11 ++++++----- contracts/Mixer.sol | 4 +--- contracts/Mocks/MerkleTreeWithHistoryMock.sol | 2 +- lib/MerkleTree.js | 5 +++-- migrations/4_deploy_eth_mixer.js | 4 ++-- migrations/5_deploy_erc20_mixer.js | 3 +-- test/ERC20Mixer.test.js | 5 +---- test/ETHMixer.test.js | 5 +---- test/MerkleTreeWithHistory.test.js | 19 ++++++------------- 13 files changed, 27 insertions(+), 46 deletions(-) diff --git a/.env.example b/.env.example index 7fdb12d..20f30fd 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,6 @@ MERKLE_TREE_HEIGHT=16 # in wei ETH_AMOUNT=100000000000000000 TOKEN_AMOUNT=100000000000000000 -EMPTY_ELEMENT=1 PRIVATE_KEY= ERC20_TOKEN= diff --git a/cli.js b/cli.js index e2e9c2b..0a23426 100755 --- a/cli.js +++ b/cli.js @@ -13,7 +13,7 @@ const buildGroth16 = require('websnark/src/groth16') const websnarkUtils = require('websnark/src/utils') let web3, mixer, erc20mixer, circuit, proving_key, groth16, erc20 -let MERKLE_TREE_HEIGHT, ETH_AMOUNT, EMPTY_ELEMENT, ERC20_TOKEN +let MERKLE_TREE_HEIGHT, ETH_AMOUNT, ERC20_TOKEN const inBrowser = (typeof window !== 'undefined') /** Generate random number of specified byte length */ @@ -83,7 +83,7 @@ async function withdrawErc20(note, receiver, relayer) { } return e.returnValues.commitment }) - const tree = new merkleTree(MERKLE_TREE_HEIGHT, EMPTY_ELEMENT, leaves) + const tree = new merkleTree(MERKLE_TREE_HEIGHT, leaves) const validRoot = await erc20mixer.methods.isKnownRoot(await tree.root()).call() const nullifierHash = pedersenHash(deposit.nullifier.leInt2Buff(31)) const nullifierHashToCheck = nullifierHash.toString(16).padStart('66', '0x000000') @@ -152,7 +152,7 @@ async function withdraw(note, receiver) { const leaves = events .sort((a, b) => a.returnValues.leafIndex.sub(b.returnValues.leafIndex)) // Sort events in chronological order .map(e => e.returnValues.commitment) - const tree = new merkleTree(MERKLE_TREE_HEIGHT, EMPTY_ELEMENT, leaves) + const tree = new merkleTree(MERKLE_TREE_HEIGHT, leaves) // Find current commitment in the tree let depositEvent = events.find(e => e.returnValues.commitment.eq(paddedCommitment)) @@ -210,7 +210,6 @@ async function init() { proving_key = await (await fetch('build/circuits/withdraw_proving_key.bin')).arrayBuffer() MERKLE_TREE_HEIGHT = 16 ETH_AMOUNT = 1e18 - EMPTY_ELEMENT = 1 } else { // Initialize from local node web3 = new Web3('http://localhost:8545', null, { transactionConfirmationBlocks: 1 }) @@ -220,7 +219,6 @@ async function init() { require('dotenv').config() MERKLE_TREE_HEIGHT = process.env.MERKLE_TREE_HEIGHT ETH_AMOUNT = process.env.ETH_AMOUNT - EMPTY_ELEMENT = process.env.EMPTY_ELEMENT ERC20_TOKEN = process.env.ERC20_TOKEN erc20ContractJson = require('./build/contracts/ERC20Mock.json') erc20mixerJson = require('./build/contracts/ERC20Mixer.json') diff --git a/contracts/ERC20Mixer.sol b/contracts/ERC20Mixer.sol index a7c6c58..46cbb35 100644 --- a/contracts/ERC20Mixer.sol +++ b/contracts/ERC20Mixer.sol @@ -20,10 +20,9 @@ contract ERC20Mixer is Mixer { IVerifier _verifier, uint256 _denomination, uint8 _merkleTreeHeight, - uint256 _emptyElement, address _operator, address _token - ) Mixer(_verifier, _denomination, _merkleTreeHeight, _emptyElement, _operator) public { + ) Mixer(_verifier, _denomination, _merkleTreeHeight, _operator) public { token = _token; } diff --git a/contracts/ETHMixer.sol b/contracts/ETHMixer.sol index 56a9aed..188dbb2 100644 --- a/contracts/ETHMixer.sol +++ b/contracts/ETHMixer.sol @@ -18,9 +18,8 @@ contract ETHMixer is Mixer { IVerifier _verifier, uint256 _denomination, uint8 _merkleTreeHeight, - uint256 _emptyElement, address _operator - ) Mixer(_verifier, _denomination, _merkleTreeHeight, _emptyElement, _operator) public { + ) Mixer(_verifier, _denomination, _merkleTreeHeight, _operator) public { } function _processWithdraw(address payable _receiver, address payable _relayer, uint256 _fee, uint256 _refund) internal { diff --git a/contracts/MerkleTreeWithHistory.sol b/contracts/MerkleTreeWithHistory.sol index 0deb5a7..39c18a6 100644 --- a/contracts/MerkleTreeWithHistory.sol +++ b/contracts/MerkleTreeWithHistory.sol @@ -18,8 +18,9 @@ library Hasher { contract MerkleTreeWithHistory { uint256 public levels; - uint256 constant FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - uint256 constant ROOT_HISTORY_SIZE = 100; + uint256 public constant FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 public constant ZERO_VALUE = 5702960885942360421128284892092891246826997279710054143430547229469817701242; // = MiMC("tornado") + uint256 public constant ROOT_HISTORY_SIZE = 100; uint256[ROOT_HISTORY_SIZE] public _roots; uint256 public current_root_index = 0; @@ -28,12 +29,12 @@ contract MerkleTreeWithHistory { uint32 public next_index = 0; - constructor(uint256 tree_levels, uint256 zero_value) public { + constructor(uint256 tree_levels) public { require(tree_levels > 0, "tree_levels should be greater than zero"); levels = tree_levels; - uint256 current_zero = zero_value; - _zeros.push(zero_value); + uint256 current_zero = ZERO_VALUE; + _zeros.push(ZERO_VALUE); _filled_subtrees.push(current_zero); for (uint8 i = 1; i < levels; i++) { diff --git a/contracts/Mixer.sol b/contracts/Mixer.sol index 223c3e0..11e2541 100644 --- a/contracts/Mixer.sol +++ b/contracts/Mixer.sol @@ -43,16 +43,14 @@ contract Mixer is MerkleTreeWithHistory { @dev The constructor @param _verifier the address of SNARK verifier for this contract @param _merkleTreeHeight the height of deposits' Merkle Tree - @param _emptyElement default element of the deposits' Merkle Tree @param _operator operator address (see operator above) */ constructor( IVerifier _verifier, uint256 _denomination, uint8 _merkleTreeHeight, - uint256 _emptyElement, address _operator - ) MerkleTreeWithHistory(_merkleTreeHeight, _emptyElement) public { + ) MerkleTreeWithHistory(_merkleTreeHeight) public { require(_denomination > 0, "denomination should be greater than 0"); verifier = _verifier; operator = _operator; diff --git a/contracts/Mocks/MerkleTreeWithHistoryMock.sol b/contracts/Mocks/MerkleTreeWithHistoryMock.sol index 0029b44..1b98af7 100644 --- a/contracts/Mocks/MerkleTreeWithHistoryMock.sol +++ b/contracts/Mocks/MerkleTreeWithHistoryMock.sol @@ -4,7 +4,7 @@ import '../MerkleTreeWithHistory.sol'; contract MerkleTreeWithHistoryMock is MerkleTreeWithHistory { - constructor (uint8 tree_levels, uint256 zero_value) MerkleTreeWithHistory(tree_levels, zero_value) public {} + constructor (uint8 tree_levels) MerkleTreeWithHistory(tree_levels) public {} function insert(uint256 leaf) public { _insert(leaf); diff --git a/lib/MerkleTree.js b/lib/MerkleTree.js index f18f6f3..05b97ff 100644 --- a/lib/MerkleTree.js +++ b/lib/MerkleTree.js @@ -1,9 +1,10 @@ const jsStorage = require('./Storage') const hasherImpl = require('./MiMC') +const { bigInt } = require('snarkjs') class MerkleTree { - constructor(n_levels, zero_value, defaultElements, prefix, storage, hasher) { + constructor(n_levels, defaultElements, prefix, storage, hasher) { this.prefix = prefix this.storage = storage || new jsStorage() this.hasher = hasher || new hasherImpl() @@ -11,7 +12,7 @@ class MerkleTree { this.zero_values = [] this.totalElements = 0 - let current_zero_value = zero_value || 0 + let current_zero_value = bigInt('5702960885942360421128284892092891246826997279710054143430547229469817701242') this.zero_values.push(current_zero_value) for (let i = 0; i < n_levels; i++) { current_zero_value = this.hasher.hash(i, current_zero_value, current_zero_value) diff --git a/migrations/4_deploy_eth_mixer.js b/migrations/4_deploy_eth_mixer.js index 6100b04..2a2d48c 100644 --- a/migrations/4_deploy_eth_mixer.js +++ b/migrations/4_deploy_eth_mixer.js @@ -7,11 +7,11 @@ const hasherContract = artifacts.require('Hasher') module.exports = function(deployer, network, accounts) { return deployer.then(async () => { - const { MERKLE_TREE_HEIGHT, ETH_AMOUNT, EMPTY_ELEMENT } = process.env + const { MERKLE_TREE_HEIGHT, ETH_AMOUNT } = process.env const verifier = await Verifier.deployed() const hasherInstance = await hasherContract.deployed() await ETHMixer.link(hasherContract, hasherInstance.address) - const mixer = await deployer.deploy(ETHMixer, verifier.address, ETH_AMOUNT, MERKLE_TREE_HEIGHT, EMPTY_ELEMENT, accounts[0]) + const mixer = await deployer.deploy(ETHMixer, verifier.address, ETH_AMOUNT, MERKLE_TREE_HEIGHT, accounts[0]) console.log('ETHMixer\'s address ', mixer.address) }) } diff --git a/migrations/5_deploy_erc20_mixer.js b/migrations/5_deploy_erc20_mixer.js index 03109fb..a4506c2 100644 --- a/migrations/5_deploy_erc20_mixer.js +++ b/migrations/5_deploy_erc20_mixer.js @@ -8,7 +8,7 @@ const ERC20Mock = artifacts.require('ERC20Mock') module.exports = function(deployer, network, accounts) { return deployer.then(async () => { - const { MERKLE_TREE_HEIGHT, EMPTY_ELEMENT, ERC20_TOKEN, TOKEN_AMOUNT } = process.env + const { MERKLE_TREE_HEIGHT, ERC20_TOKEN, TOKEN_AMOUNT } = process.env const verifier = await Verifier.deployed() const hasherInstance = await hasherContract.deployed() await ERC20Mixer.link(hasherContract, hasherInstance.address) @@ -22,7 +22,6 @@ module.exports = function(deployer, network, accounts) { verifier.address, TOKEN_AMOUNT, MERKLE_TREE_HEIGHT, - EMPTY_ELEMENT, accounts[0], token, ) diff --git a/test/ERC20Mixer.test.js b/test/ERC20Mixer.test.js index 534a062..87a0141 100644 --- a/test/ERC20Mixer.test.js +++ b/test/ERC20Mixer.test.js @@ -11,7 +11,7 @@ const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper') const Mixer = artifacts.require('./ERC20Mixer.sol') const Token = artifacts.require('./ERC20Mock.sol') const USDTToken = artifacts.require('./IUSDT.sol') -const { ETH_AMOUNT, TOKEN_AMOUNT, MERKLE_TREE_HEIGHT, EMPTY_ELEMENT, ERC20_TOKEN } = process.env +const { ETH_AMOUNT, TOKEN_AMOUNT, MERKLE_TREE_HEIGHT, ERC20_TOKEN } = process.env const websnarkUtils = require('websnark/src/utils') const buildGroth16 = require('websnark/src/groth16') @@ -50,7 +50,6 @@ contract('ERC20Mixer', accounts => { const sender = accounts[0] const operator = accounts[0] const levels = MERKLE_TREE_HEIGHT || 16 - const zeroValue = EMPTY_ELEMENT || 1337 let tokenDenomination = TOKEN_AMOUNT || '1000000000000000000' // 1 ether let snapshotId let prefix = 'test' @@ -66,7 +65,6 @@ contract('ERC20Mixer', accounts => { before(async () => { tree = new MerkleTree( levels, - zeroValue, null, prefix, ) @@ -401,7 +399,6 @@ contract('ERC20Mixer', accounts => { snapshotId = await takeSnapshot() tree = new MerkleTree( levels, - zeroValue, null, prefix, ) diff --git a/test/ETHMixer.test.js b/test/ETHMixer.test.js index aa8f3a6..595014a 100644 --- a/test/ETHMixer.test.js +++ b/test/ETHMixer.test.js @@ -9,7 +9,7 @@ const { toBN, toHex, randomHex } = require('web3-utils') const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper') const Mixer = artifacts.require('./ETHMixer.sol') -const { ETH_AMOUNT, MERKLE_TREE_HEIGHT, EMPTY_ELEMENT } = process.env +const { ETH_AMOUNT, MERKLE_TREE_HEIGHT } = process.env const websnarkUtils = require('websnark/src/utils') const buildGroth16 = require('websnark/src/groth16') @@ -62,7 +62,6 @@ contract('ETHMixer', accounts => { const sender = accounts[0] const operator = accounts[0] const levels = MERKLE_TREE_HEIGHT || 16 - const zeroValue = EMPTY_ELEMENT || 1337 const value = ETH_AMOUNT || '1000000000000000000' // 1 ether let snapshotId let prefix = 'test' @@ -78,7 +77,6 @@ contract('ETHMixer', accounts => { before(async () => { tree = new MerkleTree( levels, - zeroValue, null, prefix, ) @@ -521,7 +519,6 @@ contract('ETHMixer', accounts => { snapshotId = await takeSnapshot() tree = new MerkleTree( levels, - zeroValue, null, prefix, ) diff --git a/test/MerkleTreeWithHistory.test.js b/test/MerkleTreeWithHistory.test.js index 64c48d2..814f07c 100644 --- a/test/MerkleTreeWithHistory.test.js +++ b/test/MerkleTreeWithHistory.test.js @@ -12,7 +12,7 @@ const hasherContract = artifacts.require('./Hasher.sol') const MerkleTree = require('../lib/MerkleTree') const hasherImpl = require('../lib/MiMC') -const { ETH_AMOUNT, MERKLE_TREE_HEIGHT, EMPTY_ELEMENT } = process.env +const { ETH_AMOUNT, MERKLE_TREE_HEIGHT } = process.env // eslint-disable-next-line no-unused-vars function BNArrayToStringArray(array) { @@ -27,7 +27,6 @@ contract('MerkleTreeWithHistory', accounts => { let merkleTreeWithHistory let hasherInstance let levels = MERKLE_TREE_HEIGHT || 16 - let zeroValue = EMPTY_ELEMENT || 1337 const sender = accounts[0] // eslint-disable-next-line no-unused-vars const value = ETH_AMOUNT || '1000000000000000000' @@ -39,19 +38,19 @@ contract('MerkleTreeWithHistory', accounts => { before(async () => { tree = new MerkleTree( levels, - zeroValue, null, prefix, ) hasherInstance = await hasherContract.deployed() await MerkleTreeWithHistory.link(hasherContract, hasherInstance.address) - merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels, zeroValue) + merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels) snapshotId = await takeSnapshot() }) describe('#constructor', () => { it('should initialize', async () => { const filled_subtrees = await merkleTreeWithHistory.filled_subtrees() + const zeroValue = await merkleTreeWithHistory.ZERO_VALUE() filled_subtrees[0].should.be.eq.BN(zeroValue) const zeros = await merkleTreeWithHistory.zeros() zeros[0].should.be.eq.BN(zeroValue) @@ -70,7 +69,6 @@ contract('MerkleTreeWithHistory', accounts => { hasher = new hasherImpl() tree = new MerkleTree( 2, - zeroValue, null, prefix, ) @@ -91,7 +89,6 @@ contract('MerkleTreeWithHistory', accounts => { const batchTree = new MerkleTree( levels, - zeroValue, elements, prefix, ) @@ -131,7 +128,6 @@ contract('MerkleTreeWithHistory', accounts => { const batchTree = new MerkleTree( levels, - zeroValue, elements, prefix, ) @@ -150,7 +146,6 @@ contract('MerkleTreeWithHistory', accounts => { console.time('MerkleTree') tree = new MerkleTree( levels, - zeroValue, elements, prefix, ) @@ -177,8 +172,7 @@ contract('MerkleTreeWithHistory', accounts => { it('should reject if tree is full', async () => { levels = 6 - zeroValue = 1337 - merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels, zeroValue) + merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels) for (let i = 0; i < 2**levels; i++) { await merkleTreeWithHistory.insert(i+42).should.be.fulfilled @@ -193,8 +187,8 @@ contract('MerkleTreeWithHistory', accounts => { it.skip('hasher gas', async () => { levels = 6 - zeroValue = 1337 - merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels, zeroValue) + merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels) + const zeroValue = await merkleTreeWithHistory.zeroValue() const gas = await merkleTreeWithHistory.hashLeftRight.estimateGas(zeroValue, zeroValue) console.log('gas', gas - 21000) @@ -208,7 +202,6 @@ contract('MerkleTreeWithHistory', accounts => { hasher = new hasherImpl() tree = new MerkleTree( levels, - zeroValue, null, prefix, null, From e413ccdc29eb7a48032e03f57858ade300a1733f Mon Sep 17 00:00:00 2001 From: poma Date: Sat, 2 Nov 2019 15:48:22 +0300 Subject: [PATCH 03/10] make underscores consistent - in func args and in internal func names --- contracts/ERC20Mixer.sol | 20 ++++++++++---------- contracts/Mixer.sol | 38 +++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/contracts/ERC20Mixer.sol b/contracts/ERC20Mixer.sol index 46cbb35..8a7c7d7 100644 --- a/contracts/ERC20Mixer.sol +++ b/contracts/ERC20Mixer.sol @@ -28,30 +28,30 @@ contract ERC20Mixer is Mixer { function _processDeposit() internal { require(msg.value == 0, "ETH value is supposed to be 0 for ETH mixer"); - safeErc20TransferFrom(msg.sender, address(this), denomination); + _safeErc20TransferFrom(msg.sender, address(this), denomination); } function _processWithdraw(address payable _receiver, address payable _relayer, uint256 _fee, uint256 _refund) internal { require(msg.value == _refund, "Incorrect refund amount received by the contract"); - safeErc20Transfer(_receiver, denomination - _fee); + _safeErc20Transfer(_receiver, denomination - _fee); if (_fee > 0) { - safeErc20Transfer(_relayer, _fee); + _safeErc20Transfer(_relayer, _fee); } if (_refund > 0) { _receiver.transfer(_refund); } } - function safeErc20TransferFrom(address from, address to, uint256 amount) internal { + function _safeErc20TransferFrom(address _from, address _to, uint256 _amount) internal { bool success; bytes memory data; bytes4 transferFromSelector = 0x23b872dd; (success, data) = token.call( - abi.encodeWithSelector( - transferFromSelector, - from, to, amount - ) + abi.encodeWithSelector( + transferFromSelector, + _from, _to, _amount + ) ); require(success, "not enough allowed tokens"); @@ -64,14 +64,14 @@ contract ERC20Mixer is Mixer { } } - function safeErc20Transfer(address to, uint256 amount) internal { + function _safeErc20Transfer(address _to, uint256 _amount) internal { bool success; bytes memory data; bytes4 transferSelector = 0xa9059cbb; (success, data) = token.call( abi.encodeWithSelector( transferSelector, - to, amount + _to, _amount ) ); require(success, "not enough tokens"); diff --git a/contracts/Mixer.sol b/contracts/Mixer.sol index 11e2541..0a6a8b2 100644 --- a/contracts/Mixer.sol +++ b/contracts/Mixer.sol @@ -14,7 +14,7 @@ pragma solidity ^0.5.8; import "./MerkleTreeWithHistory.sol"; contract IVerifier { - function verifyProof(uint256[8] memory proof, uint256[6] memory input) public returns(bool); + function verifyProof(uint256[8] memory _proof, uint256[6] memory _input) public returns(bool); } contract Mixer is MerkleTreeWithHistory { @@ -59,16 +59,16 @@ contract Mixer is MerkleTreeWithHistory { /** @dev Deposit funds into mixer. The caller must send (for ETH) or approve (for ERC20) value equal to or `denomination` of this mixer. - @param commitment the note commitment, which is PedersenHash(nullifier + secret) + @param _commitment the note commitment, which is PedersenHash(nullifier + secret) */ - function deposit(uint256 commitment) public payable { + function deposit(uint256 _commitment) public payable { require(!isDepositsDisabled, "deposits are disabled"); - require(!commitments[commitment], "The commitment has been submitted"); - uint256 insertedIndex = _insert(commitment); - commitments[commitment] = true; + require(!commitments[_commitment], "The commitment has been submitted"); + uint256 insertedIndex = _insert(_commitment); + commitments[_commitment] = true; _processDeposit(); - emit Deposit(commitment, insertedIndex, block.timestamp); + emit Deposit(_commitment, insertedIndex, block.timestamp); } /** @dev this function is defined in a child contract */ @@ -82,18 +82,18 @@ contract Mixer is MerkleTreeWithHistory { - the receiver of funds - optional fee that goes to the transaction sender (usually a relay) */ - function withdraw(uint256[8] memory proof, uint256[6] memory input) public payable { - uint256 root = input[0]; - uint256 nullifierHash = input[1]; - address payable receiver = address(input[2]); - address payable relayer = address(input[3]); - uint256 fee = input[4]; - uint256 refund = input[5]; + function withdraw(uint256[8] memory _proof, uint256[6] memory _input) public payable { + uint256 root = _input[0]; + uint256 nullifierHash = _input[1]; + address payable receiver = address(_input[2]); + address payable relayer = address(_input[3]); + uint256 fee = _input[4]; + uint256 refund = _input[5]; require(fee <= denomination, "Fee exceeds transfer value"); require(!nullifierHashes[nullifierHash], "The note has been already spent"); require(isKnownRoot(root), "Cannot find your merkle root"); // Make sure to use a recent one - require(verifier.verifyProof(proof, input), "Invalid withdraw proof"); + require(verifier.verifyProof(_proof, _input), "Invalid withdraw proof"); nullifierHashes[nullifierHash] = true; _processWithdraw(receiver, relayer, fee, refund); emit Withdrawal(receiver, nullifierHash, relayer, fee); @@ -103,8 +103,8 @@ contract Mixer is MerkleTreeWithHistory { function _processWithdraw(address payable _receiver, address payable _relayer, uint256 _fee, uint256 _refund) internal; /** @dev whether a note is already spent */ - function isSpent(uint256 nullifierHash) public view returns(bool) { - return nullifierHashes[nullifierHash]; + function isSpent(uint256 _nullifierHash) public view returns(bool) { + return nullifierHashes[_nullifierHash]; } /** @@ -119,9 +119,9 @@ contract Mixer is MerkleTreeWithHistory { @dev allow operator to update SNARK verification keys. This is needed to update keys after the final trusted setup ceremony is held. After that operator is supposed to permanently disable this ability. */ - function updateVerifier(address newVerifier) external onlyOperator { + function updateVerifier(address _newVerifier) external onlyOperator { require(!isVerifierUpdateDisabled, "Verifier updates have been disabled."); - verifier = IVerifier(newVerifier); + verifier = IVerifier(_newVerifier); } /** From 2ded1f8adbe310a44069fe788e30da9accf7498e Mon Sep 17 00:00:00 2001 From: poma Date: Sat, 2 Nov 2019 16:04:17 +0300 Subject: [PATCH 04/10] refactor merkle tree naming --- contracts/MerkleTreeWithHistory.sol | 105 ++++++++---------- contracts/Mocks/MerkleTreeWithHistoryMock.sol | 6 +- test/MerkleTreeWithHistory.test.js | 8 +- 3 files changed, 53 insertions(+), 66 deletions(-) diff --git a/contracts/MerkleTreeWithHistory.sol b/contracts/MerkleTreeWithHistory.sol index 39c18a6..2015c18 100644 --- a/contracts/MerkleTreeWithHistory.sol +++ b/contracts/MerkleTreeWithHistory.sol @@ -16,123 +16,110 @@ library Hasher { } contract MerkleTreeWithHistory { - uint256 public levels; - uint256 public constant FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617; uint256 public constant ZERO_VALUE = 5702960885942360421128284892092891246826997279710054143430547229469817701242; // = MiMC("tornado") + uint256 public constant ROOT_HISTORY_SIZE = 100; - uint256[ROOT_HISTORY_SIZE] public _roots; - uint256 public current_root_index = 0; + uint256[ROOT_HISTORY_SIZE] public roots; + uint256 public currentRootIndex = 0; - uint256[] private _filled_subtrees; - uint256[] private _zeros; + uint256 public levels; + uint32 public nextIndex = 0; + uint256[] public filledSubtrees; + uint256[] public zeros; - uint32 public next_index = 0; + constructor(uint256 _treeLevels) public { + require(_treeLevels > 0, "_treeLevels should be greater than zero"); + levels = _treeLevels; - constructor(uint256 tree_levels) public { - require(tree_levels > 0, "tree_levels should be greater than zero"); - levels = tree_levels; - - uint256 current_zero = ZERO_VALUE; - _zeros.push(ZERO_VALUE); - _filled_subtrees.push(current_zero); + uint256 currentZero = ZERO_VALUE; + zeros.push(ZERO_VALUE); + filledSubtrees.push(currentZero); for (uint8 i = 1; i < levels; i++) { - current_zero = hashLeftRight(current_zero, current_zero); - _zeros.push(current_zero); - _filled_subtrees.push(current_zero); + currentZero = hashLeftRight(currentZero, currentZero); + zeros.push(currentZero); + filledSubtrees.push(currentZero); } - _roots[0] = hashLeftRight(current_zero, current_zero); + roots[0] = hashLeftRight(currentZero, currentZero); } - function hashLeftRight(uint256 left, uint256 right) public pure returns (uint256 hash) { - uint256 R = left; // left is already checked to be less than field_size by snark verifier + function hashLeftRight(uint256 _left, uint256 _right) public pure returns (uint256 hash) { + uint256 R = _left; // left is already checked to be less than field_size by snark verifier uint256 C = 0; (R, C) = Hasher.MiMCSponge(R, C, 0); - R = addmod(R, right, FIELD_SIZE); + R = addmod(R, _right, FIELD_SIZE); (R, C) = Hasher.MiMCSponge(R, C, 0); return R; } - function _insert(uint256 leaf) internal returns(uint256 index) { - uint32 current_index = next_index; - require(current_index != 2**levels, "Merkle tree is full. No more leafs can be added"); - next_index += 1; - uint256 current_level_hash = leaf; + function _insert(uint256 _leaf) internal returns(uint256 index) { + uint32 currentIndex = nextIndex; + require(currentIndex != 2**levels, "Merkle tree is full. No more leafs can be added"); + nextIndex += 1; + uint256 currentLevelHash = _leaf; uint256 left; uint256 right; for (uint256 i = 0; i < levels; i++) { - if (current_index % 2 == 0) { - left = current_level_hash; - right = _zeros[i]; + if (currentIndex % 2 == 0) { + left = currentLevelHash; + right = zeros[i]; - _filled_subtrees[i] = current_level_hash; + filledSubtrees[i] = currentLevelHash; } else { - left = _filled_subtrees[i]; - right = current_level_hash; + left = filledSubtrees[i]; + right = currentLevelHash; } - current_level_hash = hashLeftRight(left, right); + currentLevelHash = hashLeftRight(left, right); - current_index /= 2; + currentIndex /= 2; } - current_root_index = (current_root_index + 1) % ROOT_HISTORY_SIZE; - _roots[current_root_index] = current_level_hash; - return next_index - 1; + currentRootIndex = (currentRootIndex + 1) % ROOT_HISTORY_SIZE; + roots[currentRootIndex] = currentLevelHash; + return nextIndex - 1; } - function isKnownRoot(uint256 root) public view returns(bool) { - if (root == 0) { + function isKnownRoot(uint256 _root) public view returns(bool) { + if (_root == 0) { return false; } // search most recent first uint256 i; - for(i = current_root_index; i < 2**256 - 1; i--) { - if (root == _roots[i]) { + for(i = currentRootIndex; i < 2**256 - 1; i--) { + if (_root == roots[i]) { return true; } } // process the rest of roots - for(i = ROOT_HISTORY_SIZE - 1; i > current_root_index; i--) { - if (root == _roots[i]) { + for(i = ROOT_HISTORY_SIZE - 1; i > currentRootIndex; i--) { + if (_root == roots[i]) { return true; } } return false; // or we can do that in other way - // uint256 i = _current_root; + // uint256 i = currentRootIndex; // do { - // if (root == _roots[i]) { + // if (root == roots[i]) { // return true; // } // if (i == 0) { // i = ROOT_HISTORY_SIZE; // } // i--; - // } while (i != _current_root); + // } while (i != currentRootIndex); } function getLastRoot() public view returns(uint256) { - return _roots[current_root_index]; - } - - function roots() public view returns(uint256[ROOT_HISTORY_SIZE] memory) { - return _roots; - } - - function filled_subtrees() public view returns(uint256[] memory) { - return _filled_subtrees; - } - - function zeros() public view returns(uint256[] memory) { - return _zeros; + return roots[currentRootIndex]; } } diff --git a/contracts/Mocks/MerkleTreeWithHistoryMock.sol b/contracts/Mocks/MerkleTreeWithHistoryMock.sol index 1b98af7..33c37c8 100644 --- a/contracts/Mocks/MerkleTreeWithHistoryMock.sol +++ b/contracts/Mocks/MerkleTreeWithHistoryMock.sol @@ -4,9 +4,9 @@ import '../MerkleTreeWithHistory.sol'; contract MerkleTreeWithHistoryMock is MerkleTreeWithHistory { - constructor (uint8 tree_levels) MerkleTreeWithHistory(tree_levels) public {} + constructor (uint8 _treeLevels) MerkleTreeWithHistory(_treeLevels) public {} - function insert(uint256 leaf) public { - _insert(leaf); + function insert(uint256 _leaf) public { + _insert(_leaf); } } diff --git a/test/MerkleTreeWithHistory.test.js b/test/MerkleTreeWithHistory.test.js index 814f07c..279dde1 100644 --- a/test/MerkleTreeWithHistory.test.js +++ b/test/MerkleTreeWithHistory.test.js @@ -49,11 +49,11 @@ contract('MerkleTreeWithHistory', accounts => { describe('#constructor', () => { it('should initialize', async () => { - const filled_subtrees = await merkleTreeWithHistory.filled_subtrees() const zeroValue = await merkleTreeWithHistory.ZERO_VALUE() - filled_subtrees[0].should.be.eq.BN(zeroValue) - const zeros = await merkleTreeWithHistory.zeros() - zeros[0].should.be.eq.BN(zeroValue) + const firstSubtree = await merkleTreeWithHistory.filledSubtrees(0) + firstSubtree.should.be.eq.BN(zeroValue) + const firstZero = await merkleTreeWithHistory.zeros(0) + firstZero.should.be.eq.BN(zeroValue) }) }) From 8a179b921773ed0b8dcd6643e3cc7d0caff7f55e Mon Sep 17 00:00:00 2001 From: poma Date: Sat, 2 Nov 2019 16:24:17 +0300 Subject: [PATCH 05/10] added comment on checks --- contracts/MerkleTreeWithHistory.sol | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/contracts/MerkleTreeWithHistory.sol b/contracts/MerkleTreeWithHistory.sol index 2015c18..75118e8 100644 --- a/contracts/MerkleTreeWithHistory.sol +++ b/contracts/MerkleTreeWithHistory.sol @@ -46,7 +46,11 @@ contract MerkleTreeWithHistory { } function hashLeftRight(uint256 _left, uint256 _right) public pure returns (uint256 hash) { - uint256 R = _left; // left is already checked to be less than field_size by snark verifier + // those checks should never trigger in practice, because they're already performed by the snark verifier + // added for convenience if someone decides to call this function directly + require(_left < FIELD_SIZE, "_left should be inside the field"); + require(_right < FIELD_SIZE, "_right should be inside the field"); + uint256 R = _left; uint256 C = 0; (R, C) = Hasher.MiMCSponge(R, C, 0); From c00e5532994f03f8bc61f147259bc5d353a0d2c9 Mon Sep 17 00:00:00 2001 From: poma Date: Sun, 3 Nov 2019 11:25:58 +0300 Subject: [PATCH 06/10] check return data length for tokens --- contracts/ERC20Mixer.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/ERC20Mixer.sol b/contracts/ERC20Mixer.sol index 8a7c7d7..2e99231 100644 --- a/contracts/ERC20Mixer.sol +++ b/contracts/ERC20Mixer.sol @@ -57,6 +57,7 @@ contract ERC20Mixer is Mixer { // if contract returns some data let's make sure that is `true` according to standard if (data.length > 0) { + require(data.length == 32, "data length should be either 0 or 32 bytes"); assembly { success := mload(add(data, 0x20)) } @@ -78,6 +79,7 @@ contract ERC20Mixer is Mixer { // if contract returns some data let's make sure that is `true` according to standard if (data.length > 0) { + require(data.length == 32, "data length should be either 0 or 32 bytes"); assembly { success := mload(add(data, 0x20)) } From ae889b5ad216f1aa3690869f2964d93633988c18 Mon Sep 17 00:00:00 2001 From: poma Date: Mon, 4 Nov 2019 22:24:16 +0300 Subject: [PATCH 07/10] use abi.decode in token transfer --- contracts/ERC20Mixer.sol | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/contracts/ERC20Mixer.sol b/contracts/ERC20Mixer.sol index 2e99231..041d814 100644 --- a/contracts/ERC20Mixer.sol +++ b/contracts/ERC20Mixer.sol @@ -44,45 +44,25 @@ contract ERC20Mixer is Mixer { } function _safeErc20TransferFrom(address _from, address _to, uint256 _amount) internal { - bool success; - bytes memory data; - bytes4 transferFromSelector = 0x23b872dd; - (success, data) = token.call( - abi.encodeWithSelector( - transferFromSelector, - _from, _to, _amount - ) - ); + (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd /* transferFrom */, _from, _to, _amount)); require(success, "not enough allowed tokens"); // if contract returns some data let's make sure that is `true` according to standard if (data.length > 0) { require(data.length == 32, "data length should be either 0 or 32 bytes"); - assembly { - success := mload(add(data, 0x20)) - } + success = abi.decode(data, (bool)); require(success, "not enough allowed tokens"); } } function _safeErc20Transfer(address _to, uint256 _amount) internal { - bool success; - bytes memory data; - bytes4 transferSelector = 0xa9059cbb; - (success, data) = token.call( - abi.encodeWithSelector( - transferSelector, - _to, _amount - ) - ); + (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb /* transfer */, _to, _amount)); require(success, "not enough tokens"); // if contract returns some data let's make sure that is `true` according to standard if (data.length > 0) { require(data.length == 32, "data length should be either 0 or 32 bytes"); - assembly { - success := mload(add(data, 0x20)) - } + success = abi.decode(data, (bool)); require(success, "not enough tokens"); } } From 27e3121bb072580ec59be688661013c0dac358df Mon Sep 17 00:00:00 2001 From: poma Date: Mon, 4 Nov 2019 22:45:56 +0300 Subject: [PATCH 08/10] comments --- contracts/ERC20Mixer.sol | 4 ++-- contracts/ETHMixer.sol | 8 ++++---- contracts/MerkleTreeWithHistory.sol | 17 +++++++++++++---- contracts/Mixer.sol | 6 +++--- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/contracts/ERC20Mixer.sol b/contracts/ERC20Mixer.sol index 041d814..9dfed90 100644 --- a/contracts/ERC20Mixer.sol +++ b/contracts/ERC20Mixer.sol @@ -47,7 +47,7 @@ contract ERC20Mixer is Mixer { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd /* transferFrom */, _from, _to, _amount)); require(success, "not enough allowed tokens"); - // if contract returns some data let's make sure that is `true` according to standard + // if contract returns some data lets make sure that is `true` according to standard if (data.length > 0) { require(data.length == 32, "data length should be either 0 or 32 bytes"); success = abi.decode(data, (bool)); @@ -59,7 +59,7 @@ contract ERC20Mixer is Mixer { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb /* transfer */, _to, _amount)); require(success, "not enough tokens"); - // if contract returns some data let's make sure that is `true` according to standard + // if contract returns some data lets make sure that is `true` according to standard if (data.length > 0) { require(data.length == 32, "data length should be either 0 or 32 bytes"); success = abi.decode(data, (bool)); diff --git a/contracts/ETHMixer.sol b/contracts/ETHMixer.sol index 188dbb2..90984c7 100644 --- a/contracts/ETHMixer.sol +++ b/contracts/ETHMixer.sol @@ -22,6 +22,10 @@ contract ETHMixer is Mixer { ) Mixer(_verifier, _denomination, _merkleTreeHeight, _operator) public { } + function _processDeposit() internal { + require(msg.value == denomination, "Please send `mixDenomination` ETH along with transaction"); + } + function _processWithdraw(address payable _receiver, address payable _relayer, uint256 _fee, uint256 _refund) internal { // sanity checks require(msg.value == 0, "Message value is supposed to be zero for ETH mixer"); @@ -32,8 +36,4 @@ contract ETHMixer is Mixer { _relayer.transfer(_fee); } } - - function _processDeposit() internal { - require(msg.value == denomination, "Please send `mixDenomination` ETH along with transaction"); - } } diff --git a/contracts/MerkleTreeWithHistory.sol b/contracts/MerkleTreeWithHistory.sol index 75118e8..e1d1ef7 100644 --- a/contracts/MerkleTreeWithHistory.sol +++ b/contracts/MerkleTreeWithHistory.sol @@ -19,11 +19,13 @@ contract MerkleTreeWithHistory { uint256 public constant FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617; uint256 public constant ZERO_VALUE = 5702960885942360421128284892092891246826997279710054143430547229469817701242; // = MiMC("tornado") + uint256 public levels; + + // the following variables are made public for easier testing and debugging and + // are not supposed to be accessed in regular code uint256 public constant ROOT_HISTORY_SIZE = 100; uint256[ROOT_HISTORY_SIZE] public roots; uint256 public currentRootIndex = 0; - - uint256 public levels; uint32 public nextIndex = 0; uint256[] public filledSubtrees; uint256[] public zeros; @@ -45,9 +47,10 @@ contract MerkleTreeWithHistory { roots[0] = hashLeftRight(currentZero, currentZero); } + /** + @dev Hash 2 tree leaves, returns MiMC(_left, _right) + */ function hashLeftRight(uint256 _left, uint256 _right) public pure returns (uint256 hash) { - // those checks should never trigger in practice, because they're already performed by the snark verifier - // added for convenience if someone decides to call this function directly require(_left < FIELD_SIZE, "_left should be inside the field"); require(_right < FIELD_SIZE, "_right should be inside the field"); uint256 R = _left; @@ -90,6 +93,9 @@ contract MerkleTreeWithHistory { return nextIndex - 1; } + /** + @dev Whether the root is present in the root history + */ function isKnownRoot(uint256 _root) public view returns(bool) { if (_root == 0) { return false; @@ -123,6 +129,9 @@ contract MerkleTreeWithHistory { // } while (i != currentRootIndex); } + /** + @dev Returns the last root + */ function getLastRoot() public view returns(uint256) { return roots[currentRootIndex]; } diff --git a/contracts/Mixer.sol b/contracts/Mixer.sol index 0a6a8b2..7c703be 100644 --- a/contracts/Mixer.sol +++ b/contracts/Mixer.sol @@ -25,7 +25,6 @@ contract Mixer is MerkleTreeWithHistory { IVerifier public verifier; // operator can - // - receive a relayer fee // - disable new deposits in case of emergency // - update snark verification key until this ability is permanently disabled address public operator; @@ -42,8 +41,9 @@ contract Mixer is MerkleTreeWithHistory { /** @dev The constructor @param _verifier the address of SNARK verifier for this contract + @param _denomination transfer amount for each deposit @param _merkleTreeHeight the height of deposits' Merkle Tree - @param _operator operator address (see operator above) + @param _operator operator address (see operator comment above) */ constructor( IVerifier _verifier, @@ -75,7 +75,7 @@ contract Mixer is MerkleTreeWithHistory { function _processDeposit() internal; /** - @dev Withdraw deposit from the mixer. `proof` is a zkSNARK proof data, and input is an array of circuit public inputs + @dev Withdraw a deposit from the mixer. `proof` is a zkSNARK proof data, and input is an array of circuit public inputs `input` array consists of: - merkle root of all deposits in the mixer - hash of unique deposit nullifier to prevent double spends From 1fd0c7fdea84f71427c26c56f374ca1791ba75a1 Mon Sep 17 00:00:00 2001 From: poma Date: Sun, 3 Nov 2019 11:45:54 +0300 Subject: [PATCH 09/10] change zero to local variable --- contracts/MerkleTreeWithHistory.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/contracts/MerkleTreeWithHistory.sol b/contracts/MerkleTreeWithHistory.sol index e1d1ef7..b9ce9ed 100644 --- a/contracts/MerkleTreeWithHistory.sol +++ b/contracts/MerkleTreeWithHistory.sol @@ -35,7 +35,7 @@ contract MerkleTreeWithHistory { levels = _treeLevels; uint256 currentZero = ZERO_VALUE; - zeros.push(ZERO_VALUE); + zeros.push(currentZero); filledSubtrees.push(currentZero); for (uint8 i = 1; i < levels; i++) { @@ -55,12 +55,9 @@ contract MerkleTreeWithHistory { require(_right < FIELD_SIZE, "_right should be inside the field"); uint256 R = _left; uint256 C = 0; - (R, C) = Hasher.MiMCSponge(R, C, 0); - R = addmod(R, _right, FIELD_SIZE); (R, C) = Hasher.MiMCSponge(R, C, 0); - return R; } From d5b16547f703d6fee798e38ff6a42af4128f6ff3 Mon Sep 17 00:00:00 2001 From: Alexey Date: Tue, 5 Nov 2019 12:22:25 +0300 Subject: [PATCH 10/10] tidy --- contracts/MerkleTreeWithHistory.sol | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/contracts/MerkleTreeWithHistory.sol b/contracts/MerkleTreeWithHistory.sol index b9ce9ed..7043237 100644 --- a/contracts/MerkleTreeWithHistory.sol +++ b/contracts/MerkleTreeWithHistory.sol @@ -50,7 +50,7 @@ contract MerkleTreeWithHistory { /** @dev Hash 2 tree leaves, returns MiMC(_left, _right) */ - function hashLeftRight(uint256 _left, uint256 _right) public pure returns (uint256 hash) { + function hashLeftRight(uint256 _left, uint256 _right) public pure returns (uint256) { require(_left < FIELD_SIZE, "_left should be inside the field"); require(_right < FIELD_SIZE, "_right should be inside the field"); uint256 R = _left; @@ -112,18 +112,6 @@ contract MerkleTreeWithHistory { } } return false; - - // or we can do that in other way - // uint256 i = currentRootIndex; - // do { - // if (root == roots[i]) { - // return true; - // } - // if (i == 0) { - // i = ROOT_HISTORY_SIZE; - // } - // i--; - // } while (i != currentRootIndex); } /**