diff --git a/circuits/withdraw.circom b/circuits/withdraw.circom index b94fe19..27612d8 100644 --- a/circuits/withdraw.circom +++ b/circuits/withdraw.circom @@ -31,6 +31,7 @@ template Withdraw(levels, rounds) { signal input root; signal input nullifierHash; signal input receiver; // not taking part in any computations + signal input relayer; // not taking part in any computations signal input fee; // not taking part in any computations signal private input nullifier; signal private input secret; @@ -56,8 +57,10 @@ template Withdraw(levels, rounds) { // Squares are used to prevent optimizer from removing those constraints signal receiverSquare; signal feeSquare; + signal relayerSquare; receiverSquare <== receiver * receiver; feeSquare <== fee * fee; + relayerSquare <== relayer * relayer; } component main = Withdraw(16, 220); diff --git a/cli.js b/cli.js index bc88ceb..34d3f05 100755 --- a/cli.js +++ b/cli.js @@ -75,6 +75,7 @@ async function withdraw(note, receiver) { root: root, nullifierHash, receiver: bigInt(receiver), + relayer: bigInt(0), fee: bigInt(0), // private diff --git a/contracts/ERC20Mixer.sol b/contracts/ERC20Mixer.sol index b36a581..aeb1ac9 100644 --- a/contracts/ERC20Mixer.sol +++ b/contracts/ERC20Mixer.sol @@ -36,12 +36,12 @@ contract ERC20Mixer is Mixer { safeErc20TransferFrom(msg.sender, address(this), mixDenomination); } - function _processWithdraw(address payable _receiver, uint256 _fee) internal { + function _processWithdraw(address payable _receiver, address payable _relayer, uint256 _fee) internal { _receiver.transfer(userEther); safeErc20Transfer(_receiver, mixDenomination - _fee); if (_fee > 0) { - safeErc20Transfer(operator, _fee); + safeErc20Transfer(_relayer, _fee); } } diff --git a/contracts/ETHMixer.sol b/contracts/ETHMixer.sol index 5da14b9..107fe6c 100644 --- a/contracts/ETHMixer.sol +++ b/contracts/ETHMixer.sol @@ -23,10 +23,10 @@ contract ETHMixer is Mixer { ) Mixer(_verifier, _mixDenomination, _merkleTreeHeight, _emptyElement, _operator) public { } - function _processWithdraw(address payable _receiver, uint256 _fee) internal { + function _processWithdraw(address payable _receiver, address payable _relayer, uint256 _fee) internal { _receiver.transfer(mixDenomination - _fee); if (_fee > 0) { - operator.transfer(_fee); + _relayer.transfer(_fee); } } diff --git a/contracts/Mixer.sol b/contracts/Mixer.sol index 2014d84..7cae6f2 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[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[4] memory input) public returns(bool); + function verifyProof(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[5] memory input) public returns(bool); } contract Mixer is MerkleTreeWithHistory { @@ -29,7 +29,7 @@ contract Mixer is MerkleTreeWithHistory { uint256 public mixDenomination; event Deposit(uint256 indexed commitment, uint256 leafIndex, uint256 timestamp); - event Withdraw(address to, uint256 nullifierHash, uint256 fee); + event Withdraw(address to, uint256 nullifierHash, address relayer, uint256 fee); /** @dev The constructor @@ -75,19 +75,20 @@ contract Mixer is MerkleTreeWithHistory { - the receiver of funds - optional fee that goes to the transaction sender (usually a relay) */ - function withdraw(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[4] memory input) public { + function withdraw(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[5] memory input) public { uint256 root = input[0]; uint256 nullifierHash = input[1]; address payable receiver = address(input[2]); - uint256 fee = input[3]; + address payable relayer = address(input[3]); + uint256 fee = input[4]; require(fee < mixDenomination, "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(a, b, c, input), "Invalid withdraw proof"); nullifierHashes[nullifierHash] = true; - _processWithdraw(receiver, fee); - emit Withdraw(receiver, nullifierHash, fee); + _processWithdraw(receiver, relayer, fee); + emit Withdraw(receiver, nullifierHash, relayer, fee); } function toggleDeposits() external { @@ -105,6 +106,6 @@ contract Mixer is MerkleTreeWithHistory { } function _processDeposit() internal {} - function _processWithdraw(address payable _receiver, uint256 _fee) internal {} + function _processWithdraw(address payable _receiver, address payable _relayer, uint256 _fee) internal {} } diff --git a/test/ERC20Mixer.test.js b/test/ERC20Mixer.test.js index 4a8fa79..e9f72c2 100644 --- a/test/ERC20Mixer.test.js +++ b/test/ERC20Mixer.test.js @@ -127,6 +127,7 @@ contract('ERC20Mixer', accounts => { // public root, nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)), + relayer: operator, receiver, fee, @@ -167,6 +168,7 @@ contract('ERC20Mixer', accounts => { logs[0].event.should.be.equal('Withdraw') 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) isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000')) isSpent.should.be.equal(true) @@ -207,6 +209,7 @@ contract('ERC20Mixer', accounts => { // public root, nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)), + relayer: operator, receiver, fee, @@ -249,6 +252,7 @@ contract('ERC20Mixer', accounts => { logs[0].event.should.be.equal('Withdraw') 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) isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000')) isSpent.should.be.equal(true) @@ -285,6 +289,7 @@ contract('ERC20Mixer', accounts => { // public root, nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)), + relayer: operator, receiver, fee, @@ -328,6 +333,7 @@ contract('ERC20Mixer', accounts => { logs[0].event.should.be.equal('Withdraw') 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) isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000')) isSpent.should.be.equal(true) diff --git a/test/ETHMixer.test.js b/test/ETHMixer.test.js index 32a3721..cd6753b 100644 --- a/test/ETHMixer.test.js +++ b/test/ETHMixer.test.js @@ -141,6 +141,7 @@ contract('ETHMixer', accounts => { root, nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)), nullifier: deposit.nullifier, + relayer: operator, receiver, fee, secret: deposit.secret, @@ -196,6 +197,7 @@ contract('ETHMixer', accounts => { // public root, nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)), + relayer: operator, receiver, fee, @@ -235,6 +237,7 @@ contract('ETHMixer', accounts => { logs[0].event.should.be.equal('Withdraw') 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) isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000')) isSpent.should.be.equal(true) @@ -251,6 +254,7 @@ contract('ETHMixer', accounts => { root, nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)), nullifier: deposit.nullifier, + relayer: operator, receiver, fee, secret: deposit.secret, @@ -275,6 +279,7 @@ contract('ETHMixer', accounts => { root, nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)), nullifier: deposit.nullifier, + relayer: operator, receiver, fee, secret: deposit.secret, @@ -299,6 +304,7 @@ contract('ETHMixer', accounts => { root, nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)), nullifier: deposit.nullifier, + relayer: operator, receiver, fee: oneEtherFee, secret: deposit.secret, @@ -323,6 +329,7 @@ contract('ETHMixer', accounts => { nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)), root, nullifier: deposit.nullifier, + relayer: operator, receiver, fee, secret: deposit.secret, @@ -350,6 +357,7 @@ contract('ETHMixer', accounts => { root, nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)), nullifier: deposit.nullifier, + relayer: operator, receiver, fee, secret: deposit.secret,