mirror of
https://github.com/tornadocash/tornado-core.git
synced 2024-11-25 03:11:14 +01:00
return refund to a relayer in case of failure
This commit is contained in:
parent
a6519fb280
commit
ce550eea58
@ -38,8 +38,13 @@ contract ERC20Mixer is Mixer {
|
|||||||
if (_fee > 0) {
|
if (_fee > 0) {
|
||||||
_safeErc20Transfer(_relayer, _fee);
|
_safeErc20Transfer(_relayer, _fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_refund > 0) {
|
if (_refund > 0) {
|
||||||
_recipient.call.value(_refund)("");
|
(bool success, ) = _recipient.call.value(_refund)("");
|
||||||
|
if (!success) {
|
||||||
|
// let's return _refund back to the relayer
|
||||||
|
_relayer.transfer(_refund);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
contracts/Mocks/BadRecipient.sol
Normal file
7
contracts/Mocks/BadRecipient.sol
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
pragma solidity ^0.5.0;
|
||||||
|
|
||||||
|
contract BadRecipient {
|
||||||
|
function() external {
|
||||||
|
require(false, "this contract does not accept ETH");
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ const { toBN, toHex } = require('web3-utils')
|
|||||||
const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper')
|
const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper')
|
||||||
|
|
||||||
const Mixer = artifacts.require('./ERC20Mixer.sol')
|
const Mixer = artifacts.require('./ERC20Mixer.sol')
|
||||||
|
const BadRecipient = artifacts.require('./BadRecipient.sol')
|
||||||
const Token = artifacts.require('./ERC20Mock.sol')
|
const Token = artifacts.require('./ERC20Mock.sol')
|
||||||
const USDTToken = artifacts.require('./IUSDT.sol')
|
const USDTToken = artifacts.require('./IUSDT.sol')
|
||||||
const { ETH_AMOUNT, TOKEN_AMOUNT, MERKLE_TREE_HEIGHT, ERC20_TOKEN } = process.env
|
const { ETH_AMOUNT, TOKEN_AMOUNT, MERKLE_TREE_HEIGHT, ERC20_TOKEN } = process.env
|
||||||
@ -54,6 +55,7 @@ contract('ERC20Mixer', accounts => {
|
|||||||
let mixer
|
let mixer
|
||||||
let token
|
let token
|
||||||
let usdtToken
|
let usdtToken
|
||||||
|
let badRecipient
|
||||||
const sender = accounts[0]
|
const sender = accounts[0]
|
||||||
const operator = accounts[0]
|
const operator = accounts[0]
|
||||||
const levels = MERKLE_TREE_HEIGHT || 16
|
const levels = MERKLE_TREE_HEIGHT || 16
|
||||||
@ -63,7 +65,7 @@ contract('ERC20Mixer', accounts => {
|
|||||||
let tree
|
let tree
|
||||||
const fee = bigInt(ETH_AMOUNT).shr(1) || bigInt(1e17)
|
const fee = bigInt(ETH_AMOUNT).shr(1) || bigInt(1e17)
|
||||||
const refund = ETH_AMOUNT || '1000000000000000000' // 1 ether
|
const refund = ETH_AMOUNT || '1000000000000000000' // 1 ether
|
||||||
const recipient = getRandomRecipient()
|
let recipient = getRandomRecipient()
|
||||||
const relayer = accounts[1]
|
const relayer = accounts[1]
|
||||||
let groth16
|
let groth16
|
||||||
let circuit
|
let circuit
|
||||||
@ -83,6 +85,7 @@ contract('ERC20Mixer', accounts => {
|
|||||||
token = await Token.deployed()
|
token = await Token.deployed()
|
||||||
await token.mint(sender, tokenDenomination)
|
await token.mint(sender, tokenDenomination)
|
||||||
}
|
}
|
||||||
|
badRecipient = await BadRecipient.new()
|
||||||
snapshotId = await takeSnapshot()
|
snapshotId = await takeSnapshot()
|
||||||
groth16 = await buildGroth16()
|
groth16 = await buildGroth16()
|
||||||
circuit = require('../build/circuits/withdraw.json')
|
circuit = require('../build/circuits/withdraw.json')
|
||||||
@ -201,6 +204,85 @@ contract('ERC20Mixer', accounts => {
|
|||||||
isSpent.should.be.equal(true)
|
isSpent.should.be.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should return refund to the relayer is case of fail', async () => {
|
||||||
|
const deposit = generateDeposit()
|
||||||
|
const user = accounts[4]
|
||||||
|
recipient = bigInt(badRecipient.address)
|
||||||
|
await tree.insert(deposit.commitment)
|
||||||
|
await token.mint(user, tokenDenomination)
|
||||||
|
|
||||||
|
const balanceUserBefore = await token.balanceOf(user)
|
||||||
|
await token.approve(mixer.address, tokenDenomination, { from: user })
|
||||||
|
await mixer.deposit(toFixedHex(deposit.commitment), { from: user, gasPrice: '0' })
|
||||||
|
|
||||||
|
const balanceUserAfter = await token.balanceOf(user)
|
||||||
|
balanceUserAfter.should.be.eq.BN(toBN(balanceUserBefore).sub(toBN(tokenDenomination)))
|
||||||
|
|
||||||
|
const { root, path_elements, path_index } = await tree.path(0)
|
||||||
|
// Circuit input
|
||||||
|
const input = stringifyBigInts({
|
||||||
|
// public
|
||||||
|
root,
|
||||||
|
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
|
||||||
|
relayer,
|
||||||
|
recipient,
|
||||||
|
fee,
|
||||||
|
refund,
|
||||||
|
|
||||||
|
// private
|
||||||
|
nullifier: deposit.nullifier,
|
||||||
|
secret: deposit.secret,
|
||||||
|
pathElements: path_elements,
|
||||||
|
pathIndices: path_index,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||||
|
const { proof } = websnarkUtils.toSolidityInput(proofData)
|
||||||
|
|
||||||
|
const balanceMixerBefore = await token.balanceOf(mixer.address)
|
||||||
|
const balanceRelayerBefore = await token.balanceOf(relayer)
|
||||||
|
const balanceRecieverBefore = await token.balanceOf(toHex(recipient.toString()))
|
||||||
|
|
||||||
|
const ethBalanceOperatorBefore = await web3.eth.getBalance(operator)
|
||||||
|
const ethBalanceRecieverBefore = await web3.eth.getBalance(toHex(recipient.toString()))
|
||||||
|
const ethBalanceRelayerBefore = await web3.eth.getBalance(relayer)
|
||||||
|
let isSpent = await mixer.isSpent(toFixedHex(input.nullifierHash))
|
||||||
|
isSpent.should.be.equal(false)
|
||||||
|
|
||||||
|
const args = [
|
||||||
|
toFixedHex(input.root),
|
||||||
|
toFixedHex(input.nullifierHash),
|
||||||
|
toFixedHex(input.recipient, 20),
|
||||||
|
toFixedHex(input.relayer, 20),
|
||||||
|
toFixedHex(input.fee),
|
||||||
|
toFixedHex(input.refund)
|
||||||
|
]
|
||||||
|
const { logs } = await mixer.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' })
|
||||||
|
|
||||||
|
const balanceMixerAfter = await token.balanceOf(mixer.address)
|
||||||
|
const balanceRelayerAfter = await token.balanceOf(relayer)
|
||||||
|
const ethBalanceOperatorAfter = await web3.eth.getBalance(operator)
|
||||||
|
const balanceRecieverAfter = await token.balanceOf(toHex(recipient.toString()))
|
||||||
|
const ethBalanceRecieverAfter = await web3.eth.getBalance(toHex(recipient.toString()))
|
||||||
|
const ethBalanceRelayerAfter = await web3.eth.getBalance(relayer)
|
||||||
|
const feeBN = toBN(fee.toString())
|
||||||
|
balanceMixerAfter.should.be.eq.BN(toBN(balanceMixerBefore).sub(toBN(tokenDenomination)))
|
||||||
|
balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore).add(feeBN))
|
||||||
|
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination).sub(feeBN)))
|
||||||
|
|
||||||
|
ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore))
|
||||||
|
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore))
|
||||||
|
ethBalanceRelayerAfter.should.be.eq.BN(toBN(ethBalanceRelayerBefore))
|
||||||
|
|
||||||
|
logs[0].event.should.be.equal('Withdrawal')
|
||||||
|
logs[0].args.nullifierHash.should.be.equal(toFixedHex(input.nullifierHash))
|
||||||
|
logs[0].args.relayer.should.be.eq.BN(relayer)
|
||||||
|
logs[0].args.fee.should.be.eq.BN(feeBN)
|
||||||
|
isSpent = await mixer.isSpent(toFixedHex(input.nullifierHash))
|
||||||
|
isSpent.should.be.equal(true)
|
||||||
|
})
|
||||||
|
|
||||||
it('should reject with wrong refund value', async () => {
|
it('should reject with wrong refund value', async () => {
|
||||||
const deposit = generateDeposit()
|
const deposit = generateDeposit()
|
||||||
const user = accounts[4]
|
const user = accounts[4]
|
||||||
|
Loading…
Reference in New Issue
Block a user