diff --git a/contracts/MerkleTreeWithHistory.sol b/contracts/MerkleTreeWithHistory.sol index 05f4da4..a374bff 100644 --- a/contracts/MerkleTreeWithHistory.sol +++ b/contracts/MerkleTreeWithHistory.sol @@ -1,3 +1,14 @@ +// https://tornado.cash +/* +* d888888P dP a88888b. dP +* 88 88 d8' `88 88 +* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b. +* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88 +* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88 +* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP +* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo +*/ + pragma solidity ^0.5.8; library MiMC { diff --git a/contracts/Mixer.sol b/contracts/Mixer.sol index 7c802bd..ae140ba 100644 --- a/contracts/Mixer.sol +++ b/contracts/Mixer.sol @@ -1,3 +1,14 @@ +// https://tornado.cash +/* +* d888888P dP a88888b. dP +* 88 88 d8' `88 88 +* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b. +* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88 +* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88 +* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP +* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo +*/ + pragma solidity ^0.5.8; import "./MerkleTreeWithHistory.sol"; @@ -9,7 +20,9 @@ contract IVerifier { contract Mixer is MerkleTreeWithHistory { uint256 public transferValue; bool public isDepositsEnabled = true; - address public pauseAccount; + // operator can disable new deposits in case of emergency + // it also receives a relayer fee + address payable public operator; mapping(uint256 => bool) public nullifierHashes; // we store all commitments just to prevent accidental deposits with the same commitment mapping(uint256 => bool) public commitments; @@ -28,11 +41,11 @@ contract Mixer is MerkleTreeWithHistory { uint256 _transferValue, uint8 _merkleTreeHeight, uint256 _emptyElement, - address _pauseAccount + address payable _operator ) MerkleTreeWithHistory(_merkleTreeHeight, _emptyElement) public { verifier = IVerifier(_verifier); transferValue = _transferValue; - pauseAccount = _pauseAccount; + operator = _operator; } /** @@ -62,27 +75,27 @@ contract Mixer is MerkleTreeWithHistory { address payable receiver = address(input[2]); uint256 fee = input[3]; - require(fee < transferValue, "Fee exceeds transfer value"); require(!nullifierHashes[nullifierHash], "The note has been already spent"); + require(fee < transferValue, "Fee exceeds transfer value"); require(isKnownRoot(root), "Cannot find your merkle root"); // Make sure to use a recent one require(verifier.verifyProof(a, b, c, input), "Invalid withdraw proof"); nullifierHashes[nullifierHash] = true; receiver.transfer(transferValue - fee); if (fee > 0) { - msg.sender.transfer(fee); + operator.transfer(fee); } emit Withdraw(receiver, nullifierHash, fee); } function toggleDeposits() external { - require(msg.sender == pauseAccount, "unauthorized"); + require(msg.sender == operator, "unauthorized"); isDepositsEnabled = !isDepositsEnabled; } - function setPauseAccount(address _newAccount) external { - require(msg.sender == pauseAccount, "unauthorized"); - pauseAccount = _newAccount; + function changeOperator(address payable _newAccount) external { + require(msg.sender == operator, "unauthorized"); + operator = _newAccount; } function isSpent(uint256 nullifier) public view returns(bool) { diff --git a/test/Mixer.test.js b/test/Mixer.test.js index 7b66669..1d18520 100644 --- a/test/Mixer.test.js +++ b/test/Mixer.test.js @@ -60,6 +60,7 @@ function snarkVerify(proof) { contract('Mixer', accounts => { let mixer const sender = accounts[0] + const operator = accounts[0] const levels = MERKLE_TREE_HEIGHT || 16 const zeroValue = EMPTY_ELEMENT || 1337 const value = AMOUNT || '1000000000000000000' // 1 ether @@ -211,6 +212,7 @@ contract('Mixer', accounts => { const balanceMixerBefore = await web3.eth.getBalance(mixer.address) const balanceRelayerBefore = await web3.eth.getBalance(relayer) + const balanceOperatorBefore = await web3.eth.getBalance(operator) const balanceRecieverBefore = await web3.eth.getBalance(toHex(receiver.toString())) let isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000')) isSpent.should.be.equal(false) @@ -222,10 +224,12 @@ contract('Mixer', accounts => { const balanceMixerAfter = await web3.eth.getBalance(mixer.address) const balanceRelayerAfter = await web3.eth.getBalance(relayer) + const balanceOperatorAfter = await web3.eth.getBalance(operator) const balanceRecieverAfter = await web3.eth.getBalance(toHex(receiver.toString())) const feeBN = toBN(fee.toString()) balanceMixerAfter.should.be.eq.BN(toBN(balanceMixerBefore).sub(toBN(value))) - balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore).add(feeBN)) + balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore)) + balanceOperatorAfter.should.be.eq.BN(toBN(balanceOperatorBefore).add(feeBN)) balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(value)).sub(feeBN)) @@ -386,6 +390,29 @@ contract('Mixer', accounts => { }) }) + describe('#changeOperator', () => { + it('should work', async () => { + let operator = await mixer.operator() + operator.should.be.equal(sender) + + const newOperator = accounts[7] + await mixer.changeOperator(newOperator).should.be.fulfilled + + operator = await mixer.operator() + operator.should.be.equal(newOperator) + }) + + it('cannot change from different address', async () => { + let operator = await mixer.operator() + operator.should.be.equal(sender) + + const newOperator = accounts[7] + const error = await mixer.changeOperator(newOperator, { from: accounts[7] }).should.be.rejected + error.reason.should.be.equal('unauthorized') + + }) + }) + afterEach(async () => { await revertSnapshot(snapshotId.result) // eslint-disable-next-line require-atomic-updates diff --git a/truffle-config.js b/truffle-config.js index 10df272..fc76fc8 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -55,7 +55,7 @@ module.exports = { provider: () => new HDWalletProvider(process.env.PRIVATE_KEY, 'https://mainnet.infura.io/v3/c7463beadf2144e68646ff049917b716'), network_id: 1, gas: 5000000, - gasPrice: utils.toWei('2', 'gwei'), + gasPrice: utils.toWei('3.1', 'gwei'), // confirmations: 0, // timeoutBlocks: 200, skipDryRun: true