rename variables

This commit is contained in:
poma 2019-12-13 20:49:19 +07:00
parent d9f4b16076
commit 4114f7b52c
No known key found for this signature in database
GPG Key ID: BA20CB01FE165657
13 changed files with 169 additions and 169 deletions

6
.gitignore vendored
View File

@ -1,7 +1,7 @@
build build
.vscode .vscode
/index.js /index.js
Mixer_flat.sol Tornado_flat.sol
# Created by .ignore support plugin (hsz.mobi) # Created by .ignore support plugin (hsz.mobi)
### Node template ### Node template
@ -94,5 +94,5 @@ typings/
# DynamoDB Local files # DynamoDB Local files
.dynamodb/ .dynamodb/
ERC20Mixer_flat.sol ERC20Tornado_flat.sol
ETHMixer_flat.sol ETHTornado_flat.sol

View File

@ -1,6 +1,6 @@
# Tornado mixer [![Build Status](https://travis-ci.org/tornadocash/tornado-core.svg?branch=master)](https://travis-ci.org/tornadocash/tornado-core) # Tornado Privacy Solution [![Build Status](https://travis-ci.org/tornadocash/tornado-core.svg?branch=master)](https://travis-ci.org/tornadocash/tornado-core)
Tornado is a non-custodial Ethereum and ERC20 mixer based on zkSNARKs. It improves transaction privacy by breaking the on-chain link between recipient and destination addresses. It uses a smart contract that accepts ETH deposits that can be withdrawn by a different address. Whenever ETH is withdrawn by the new address, there is no way to link the withdrawal to the deposit, ensuring complete privacy. Tornado is a non-custodial Ethereum and ERC20 privacy solution based on zkSNARKs. It improves transaction privacy by breaking the on-chain link between recipient and destination addresses. It uses a smart contract that accepts ETH deposits that can be withdrawn by a different address. Whenever ETH is withdrawn by the new address, there is no way to link the withdrawal to the deposit, ensuring complete privacy.
To make a deposit user generates a secret and sends its hash (called a commitment) along with the deposit amount to the Tornado smart contract. The contract accepts the deposit and adds the commitment to its list of deposits. To make a deposit user generates a secret and sends its hash (called a commitment) along with the deposit amount to the Tornado smart contract. The contract accepts the deposit and adds the commitment to its list of deposits.
@ -15,12 +15,12 @@ You can read more about it in [this medium article](https://medium.com/@tornado.
- Circuit Proof time = 6116ms (1071 + 347 * tree_depth) - Circuit Proof time = 6116ms (1071 + 347 * tree_depth)
- Serverless - Serverless
![mixer image](./mixer.png) ![image](diagram.png)
## Security risks ## Security risks
* Cryptographic tools used by mixer (zkSNARKS, Pedersen commitment, MiMC hash) are yet NOT extensively audited by cryptographic experts and may be vulnerable * Cryptographic tools used by Tornado (zkSNARKS, Pedersen commitment, MiMC hash) are yet NOT extensively audited by cryptographic experts and may be vulnerable
* Note: we use MiMC hash only for merkle tree, so even if a preimage attack on MiMC is discovered, it will not allow to deanonymize users. To drain funds attacker needs to be able to generate arbitrary hash collisions, which is a pretty strong assumption. * Note: we use MiMC hash only for merkle tree, so even if a preimage attack on MiMC is discovered, it will not allow to deanonymize users. To drain funds attacker needs to be able to generate arbitrary hash collisions, which is a pretty strong assumption.
* Bugs in contract. Even though we have an extensive experience in smart contract security audits, we can still make mistakes. An external audit is needed to reduce probablility of bugs. Our mixer is currently being audited, stay tuned. * Bugs in contract. Even though we have an extensive experience in smart contract security audits, we can still make mistakes. An external audit is needed to reduce probablility of bugs. Our code is currently being audited, stay tuned.
* Relayer is frontrunnable. When relayer submits a transaction someone can see it in tx pool and frontrun it with higher gas price to get the fee and drain relayer funds. * Relayer is frontrunnable. When relayer submits a transaction someone can see it in tx pool and frontrun it with higher gas price to get the fee and drain relayer funds.
* Workaround: we can set high gas price so that (almost) all fee is used on gas * Workaround: we can set high gas price so that (almost) all fee is used on gas
* Second workaround: allow only single hardcoded relayer, we use this approach for now * Second workaround: allow only single hardcoded relayer, we use this approach for now
@ -51,13 +51,13 @@ Use browser version on Kovan:
1. Open `localhost:8080` 1. Open `localhost:8080`
Use with command line version with Ganache: Use with command line version with Ganache:
### ETHMixer ### ETHTornado
1. `npm run migrate:dev` 1. `npm run migrate:dev`
1. `./cli.js deposit` 1. `./cli.js deposit`
1. `./cli.js withdraw <note from previous step> <destination eth address>` 1. `./cli.js withdraw <note from previous step> <destination eth address>`
1. `./cli.js balance <destination eth address>` 1. `./cli.js balance <destination eth address>`
### ERC20Mixer ### ERC20Tornado
1. `npm run migrate:dev` 1. `npm run migrate:dev`
1. `./cli.js depositErc20` 1. `./cli.js depositErc20`
1. `./cli.js withdrawErc20 <note from previous step> <destination eth address> <relayer eth address>` 1. `./cli.js withdrawErc20 <note from previous step> <destination eth address> <relayer eth address>`
@ -76,7 +76,7 @@ If you want, you can point the app to existing tornado contracts on Mainnet or K
1. `npx truffle migrate --network kovan --reset --f 2 --to 3` 1. `npx truffle migrate --network kovan --reset --f 2 --to 3`
1. `npx truffle migrate --network kovan --reset --f 5` 1. `npx truffle migrate --network kovan --reset --f 5`
**Note**. If you want to reuse the same verifier for all the mixers, then after you deployed one of the mixers you should only run 4th or 5th migration for ETH or ERC20 mixers respectively (`--f 4 --to 4` or `--f 5`). **Note**. If you want to reuse the same verifier for all the instances, then after you deployed one of the instances you should only run 4th or 5th migration for ETH or ERC20 contracts respectively (`--f 4 --to 4` or `--f 5`).
## Credits ## Credits

64
cli.js
View File

@ -14,7 +14,7 @@ const buildGroth16 = require('websnark/src/groth16')
const websnarkUtils = require('websnark/src/utils') const websnarkUtils = require('websnark/src/utils')
const { toWei, fromWei } = require('web3-utils') const { toWei, fromWei } = require('web3-utils')
let web3, mixer, erc20mixer, circuit, proving_key, groth16, erc20, senderAccount let web3, tornado, erc20tornado, circuit, proving_key, groth16, erc20, senderAccount
let MERKLE_TREE_HEIGHT, ETH_AMOUNT, TOKEN_AMOUNT, ERC20_TOKEN let MERKLE_TREE_HEIGHT, ETH_AMOUNT, TOKEN_AMOUNT, ERC20_TOKEN
/** Whether we are in a browser or node.js */ /** Whether we are in a browser or node.js */
@ -56,7 +56,7 @@ async function deposit() {
const deposit = createDeposit(rbigint(31), rbigint(31)) const deposit = createDeposit(rbigint(31), rbigint(31))
console.log('Submitting deposit transaction') console.log('Submitting deposit transaction')
await mixer.methods.deposit(toHex(deposit.commitment)).send({ value: ETH_AMOUNT, from: senderAccount, gas:1e6 }) await tornado.methods.deposit(toHex(deposit.commitment)).send({ value: ETH_AMOUNT, from: senderAccount, gas:1e6 })
const note = toHex(deposit.preimage, 62) const note = toHex(deposit.preimage, 62)
console.log('Your note:', note) console.log('Your note:', note)
@ -75,10 +75,10 @@ async function depositErc20() {
} }
console.log('Approving tokens for deposit') console.log('Approving tokens for deposit')
await erc20.methods.approve(erc20mixer._address, TOKEN_AMOUNT).send({ from: senderAccount, gas:1e6 }) await erc20.methods.approve(erc20tornado._address, TOKEN_AMOUNT).send({ from: senderAccount, gas:1e6 })
console.log('Submitting deposit transaction') console.log('Submitting deposit transaction')
await erc20mixer.methods.deposit(toHex(deposit.commitment)).send({ from: senderAccount, gas:1e6 }) await erc20tornado.methods.deposit(toHex(deposit.commitment)).send({ from: senderAccount, gas:1e6 })
const note = toHex(deposit.preimage, 62) const note = toHex(deposit.preimage, 62)
console.log('Your note:', note) console.log('Your note:', note)
@ -89,12 +89,12 @@ async function depositErc20() {
* Generate merkle tree for a deposit. * Generate merkle tree for a deposit.
* Download deposit events from the contract, reconstructs merkle tree, finds our deposit leaf * Download deposit events from the contract, reconstructs merkle tree, finds our deposit leaf
* in it and generates merkle proof * in it and generates merkle proof
* @param contract Mixer contract address * @param contract Tornado contract address
* @param deposit Deposit object * @param deposit Deposit object
*/ */
async function generateMerkleProof(contract, deposit) { async function generateMerkleProof(contract, deposit) {
// Get all deposit events from smart contract and assemble merkle tree from them // Get all deposit events from smart contract and assemble merkle tree from them
console.log('Getting current state from mixer contract') console.log('Getting current state from tornado contract')
const events = await contract.getPastEvents('Deposit', { fromBlock: contract.deployedBlock, toBlock: 'latest' }) const events = await contract.getPastEvents('Deposit', { fromBlock: contract.deployedBlock, toBlock: 'latest' })
const leaves = events const leaves = events
.sort((a, b) => a.returnValues.leafIndex - b.returnValues.leafIndex) // Sort events in chronological order .sort((a, b) => a.returnValues.leafIndex - b.returnValues.leafIndex) // Sort events in chronological order
@ -118,7 +118,7 @@ async function generateMerkleProof(contract, deposit) {
/** /**
* Generate SNARK proof for withdrawal * Generate SNARK proof for withdrawal
* @param contract Mixer contract address * @param contract Tornado contract address
* @param note Note string * @param note Note string
* @param recipient Funds recipient * @param recipient Funds recipient
* @param relayer Relayer address * @param relayer Relayer address
@ -174,10 +174,10 @@ async function generateProof(contract, note, recipient, relayer = 0, fee = 0, re
* @param recipient Recipient address * @param recipient Recipient address
*/ */
async function withdraw(note, recipient) { async function withdraw(note, recipient) {
const { proof, args } = await generateProof(mixer, note, recipient) const { proof, args } = await generateProof(tornado, note, recipient)
console.log('Submitting withdraw transaction') console.log('Submitting withdraw transaction')
await mixer.methods.withdraw(proof, ...args).send({ from: senderAccount, gas: 1e6 }) await tornado.methods.withdraw(proof, ...args).send({ from: senderAccount, gas: 1e6 })
console.log('Done') console.log('Done')
} }
@ -187,10 +187,10 @@ async function withdraw(note, recipient) {
* @param recipient Recipient address * @param recipient Recipient address
*/ */
async function withdrawErc20(note, recipient) { async function withdrawErc20(note, recipient) {
const { proof, args } = await generateProof(erc20mixer, note, recipient) const { proof, args } = await generateProof(erc20tornado, note, recipient)
console.log('Submitting withdraw transaction') console.log('Submitting withdraw transaction')
await erc20mixer.methods.withdraw(proof, ...args).send({ from: senderAccount, gas: 1e6 }) await erc20tornado.methods.withdraw(proof, ...args).send({ from: senderAccount, gas: 1e6 })
console.log('Done') console.log('Done')
} }
@ -207,10 +207,10 @@ async function withdrawRelay(note, recipient, relayUrl) {
console.log('Relay address: ', relayerAddress) console.log('Relay address: ', relayerAddress)
const fee = bigInt(toWei(gasPrices.fast.toString(), 'gwei')).mul(bigInt(1e6)) const fee = bigInt(toWei(gasPrices.fast.toString(), 'gwei')).mul(bigInt(1e6))
const { proof, args } = await generateProof(mixer, note, recipient, relayerAddress, fee) const { proof, args } = await generateProof(tornado, note, recipient, relayerAddress, fee)
console.log('Sending withdraw transaction through relay') console.log('Sending withdraw transaction through relay')
const resp2 = await axios.post(relayUrl + '/relay', { contract: mixer._address, proof: { proof, publicSignals: args } }) const resp2 = await axios.post(relayUrl + '/relay', { contract: tornado._address, proof: { proof, publicSignals: args } })
console.log(`Transaction submitted through relay, tx hash: ${resp2.data.txHash}`) console.log(`Transaction submitted through relay, tx hash: ${resp2.data.txHash}`)
let receipt = await waitForTxReceipt(resp2.data.txHash) let receipt = await waitForTxReceipt(resp2.data.txHash)
@ -232,10 +232,10 @@ async function withdrawRelayErc20(note, recipient, relayUrl) {
const refund = bigInt(toWei('0.001')) const refund = bigInt(toWei('0.001'))
const fee = bigInt(toWei(gasPrices.fast.toString(), 'gwei')).mul(bigInt(1e6)).add(refund).mul(bigInt(fromWei(ethPriceInDai.toString()))) const fee = bigInt(toWei(gasPrices.fast.toString(), 'gwei')).mul(bigInt(1e6)).add(refund).mul(bigInt(fromWei(ethPriceInDai.toString())))
const { proof, args } = await generateProof(erc20mixer, note, recipient, relayerAddress, fee, refund) const { proof, args } = await generateProof(erc20tornado, note, recipient, relayerAddress, fee, refund)
console.log('Sending withdraw transaction through relay') console.log('Sending withdraw transaction through relay')
const resp2 = await axios.post(relayUrl + '/relay', { contract: erc20mixer._address, proof: { proof, publicSignals: args } }) const resp2 = await axios.post(relayUrl + '/relay', { contract: erc20tornado._address, proof: { proof, publicSignals: args } })
console.log(`Transaction submitted through relay, tx hash: ${resp2.data.txHash}`) console.log(`Transaction submitted through relay, tx hash: ${resp2.data.txHash}`)
let receipt = await waitForTxReceipt(resp2.data.txHash) let receipt = await waitForTxReceipt(resp2.data.txHash)
@ -271,12 +271,12 @@ function waitForTxReceipt(txHash, attempts = 60, delay = 1000) {
* Init web3, contracts, and snark * Init web3, contracts, and snark
*/ */
async function init() { async function init() {
let contractJson, erc20ContractJson, erc20mixerJson let contractJson, erc20ContractJson, erc20tornadoJson
if (inBrowser) { if (inBrowser) {
// Initialize using injected web3 (Metamask) // Initialize using injected web3 (Metamask)
// To assemble web version run `npm run browserify` // To assemble web version run `npm run browserify`
web3 = new Web3(window.web3.currentProvider, null, { transactionConfirmationBlocks: 1 }) web3 = new Web3(window.web3.currentProvider, null, { transactionConfirmationBlocks: 1 })
contractJson = await (await fetch('build/contracts/ETHMixer.json')).json() contractJson = await (await fetch('build/contracts/ETHTornado.json')).json()
circuit = await (await fetch('build/circuits/withdraw.json')).json() circuit = await (await fetch('build/circuits/withdraw.json')).json()
proving_key = await (await fetch('build/circuits/withdraw_proving_key.bin')).arrayBuffer() proving_key = await (await fetch('build/circuits/withdraw_proving_key.bin')).arrayBuffer()
MERKLE_TREE_HEIGHT = 16 MERKLE_TREE_HEIGHT = 16
@ -285,7 +285,7 @@ async function init() {
} else { } else {
// Initialize from local node // Initialize from local node
web3 = new Web3('http://localhost:8545', null, { transactionConfirmationBlocks: 1 }) web3 = new Web3('http://localhost:8545', null, { transactionConfirmationBlocks: 1 })
contractJson = require('./build/contracts/ETHMixer.json') contractJson = require('./build/contracts/ETHTornado.json')
circuit = require('./build/circuits/withdraw.json') circuit = require('./build/circuits/withdraw.json')
proving_key = fs.readFileSync('build/circuits/withdraw_proving_key.bin').buffer proving_key = fs.readFileSync('build/circuits/withdraw_proving_key.bin').buffer
require('dotenv').config() require('dotenv').config()
@ -294,19 +294,19 @@ async function init() {
TOKEN_AMOUNT = process.env.TOKEN_AMOUNT TOKEN_AMOUNT = process.env.TOKEN_AMOUNT
ERC20_TOKEN = process.env.ERC20_TOKEN ERC20_TOKEN = process.env.ERC20_TOKEN
erc20ContractJson = require('./build/contracts/ERC20Mock.json') erc20ContractJson = require('./build/contracts/ERC20Mock.json')
erc20mixerJson = require('./build/contracts/ERC20Mixer.json') erc20tornadoJson = require('./build/contracts/ERC20Tornado.json')
} }
groth16 = await buildGroth16() groth16 = await buildGroth16()
let netId = await web3.eth.net.getId() let netId = await web3.eth.net.getId()
if (contractJson.networks[netId]) { if (contractJson.networks[netId]) {
const tx = await web3.eth.getTransaction(contractJson.networks[netId].transactionHash) const tx = await web3.eth.getTransaction(contractJson.networks[netId].transactionHash)
mixer = new web3.eth.Contract(contractJson.abi, contractJson.networks[netId].address) tornado = new web3.eth.Contract(contractJson.abi, contractJson.networks[netId].address)
mixer.deployedBlock = tx.blockNumber tornado.deployedBlock = tx.blockNumber
} }
const tx3 = await web3.eth.getTransaction(erc20mixerJson.networks[netId].transactionHash) const tx3 = await web3.eth.getTransaction(erc20tornadoJson.networks[netId].transactionHash)
erc20mixer = new web3.eth.Contract(erc20mixerJson.abi, erc20mixerJson.networks[netId].address) erc20tornado = new web3.eth.Contract(erc20tornadoJson.abi, erc20tornadoJson.networks[netId].address)
erc20mixer.deployedBlock = tx3.blockNumber erc20tornado.deployedBlock = tx3.blockNumber
if(ERC20_TOKEN === '') { if(ERC20_TOKEN === '') {
erc20 = new web3.eth.Contract(erc20ContractJson.abi, erc20ContractJson.networks[netId].address) erc20 = new web3.eth.Contract(erc20ContractJson.abi, erc20ContractJson.networks[netId].address)
@ -357,10 +357,10 @@ async function runConsole(args) {
case 'deposit': case 'deposit':
if (args.length === 1) { if (args.length === 1) {
await init() await init()
await printBalance(mixer._address, 'Mixer') await printBalance(tornado._address, 'Tornado')
await printBalance(senderAccount, 'Sender account') await printBalance(senderAccount, 'Sender account')
await deposit() await deposit()
await printBalance(mixer._address, 'Mixer') await printBalance(tornado._address, 'Tornado')
await printBalance(senderAccount, 'Sender account') await printBalance(senderAccount, 'Sender account')
} else { } else {
printHelp(1) printHelp(1)
@ -369,10 +369,10 @@ async function runConsole(args) {
case 'depositErc20': case 'depositErc20':
if (args.length === 1) { if (args.length === 1) {
await init() await init()
await printBalance(erc20mixer._address, 'Mixer') await printBalance(erc20tornado._address, 'Tornado')
await printBalance(senderAccount, 'Sender account') await printBalance(senderAccount, 'Sender account')
await depositErc20() await depositErc20()
await printBalance(erc20mixer._address, 'Mixer') await printBalance(erc20tornado._address, 'Tornado')
await printBalance(senderAccount, 'Sender account') await printBalance(senderAccount, 'Sender account')
} else { } else {
printHelp(1) printHelp(1)
@ -389,14 +389,14 @@ async function runConsole(args) {
case 'withdraw': case 'withdraw':
if (args.length >= 3 && args.length <= 4 && /^0x[0-9a-fA-F]{124}$/.test(args[1]) && /^0x[0-9a-fA-F]{40}$/.test(args[2])) { if (args.length >= 3 && args.length <= 4 && /^0x[0-9a-fA-F]{124}$/.test(args[1]) && /^0x[0-9a-fA-F]{40}$/.test(args[2])) {
await init() await init()
await printBalance(mixer._address, 'Mixer') await printBalance(tornado._address, 'Tornado')
await printBalance(args[2], 'Recipient account') await printBalance(args[2], 'Recipient account')
if (args[3]) { if (args[3]) {
await withdrawRelay(args[1], args[2], args[3]) await withdrawRelay(args[1], args[2], args[3])
} else { } else {
await withdraw(args[1], args[2]) await withdraw(args[1], args[2])
} }
await printBalance(mixer._address, 'Mixer') await printBalance(tornado._address, 'Tornado')
await printBalance(args[2], 'Recipient account') await printBalance(args[2], 'Recipient account')
} else { } else {
printHelp(1) printHelp(1)
@ -405,14 +405,14 @@ async function runConsole(args) {
case 'withdrawErc20': case 'withdrawErc20':
if (args.length >= 3 && args.length <= 4 && /^0x[0-9a-fA-F]{124}$/.test(args[1]) && /^0x[0-9a-fA-F]{40}$/.test(args[2])) { if (args.length >= 3 && args.length <= 4 && /^0x[0-9a-fA-F]{124}$/.test(args[1]) && /^0x[0-9a-fA-F]{40}$/.test(args[2])) {
await init() await init()
await printBalance(erc20mixer._address, 'Mixer') await printBalance(erc20tornado._address, 'Tornado')
await printBalance(args[2], 'Recipient account') await printBalance(args[2], 'Recipient account')
if (args[3]) { if (args[3]) {
await withdrawRelayErc20(args[1], args[2], args[3]) await withdrawRelayErc20(args[1], args[2], args[3])
} else { } else {
await withdrawErc20(args[1], args[2]) await withdrawErc20(args[1], args[2])
} }
await printBalance(erc20mixer._address, 'Mixer') await printBalance(erc20tornado._address, 'Tornado')
await printBalance(args[2], 'Recipient account') await printBalance(args[2], 'Recipient account')
} else { } else {
printHelp(1) printHelp(1)

View File

@ -11,9 +11,9 @@
pragma solidity ^0.5.8; pragma solidity ^0.5.8;
import "./Mixer.sol"; import "./Tornado.sol";
contract ERC20Mixer is Mixer { contract ERC20Tornado is Tornado {
address public token; address public token;
constructor( constructor(
@ -22,12 +22,12 @@ contract ERC20Mixer is Mixer {
uint32 _merkleTreeHeight, uint32 _merkleTreeHeight,
address _operator, address _operator,
address _token address _token
) Mixer(_verifier, _denomination, _merkleTreeHeight, _operator) public { ) Tornado(_verifier, _denomination, _merkleTreeHeight, _operator) public {
token = _token; token = _token;
} }
function _processDeposit() internal { function _processDeposit() internal {
require(msg.value == 0, "ETH value is supposed to be 0 for ERC20 mixer"); require(msg.value == 0, "ETH value is supposed to be 0 for ERC20 instance");
_safeErc20TransferFrom(msg.sender, address(this), denomination); _safeErc20TransferFrom(msg.sender, address(this), denomination);
} }

View File

@ -11,15 +11,15 @@
pragma solidity ^0.5.8; pragma solidity ^0.5.8;
import "./Mixer.sol"; import "./Tornado.sol";
contract ETHMixer is Mixer { contract ETHTornado is Tornado {
constructor( constructor(
IVerifier _verifier, IVerifier _verifier,
uint256 _denomination, uint256 _denomination,
uint32 _merkleTreeHeight, uint32 _merkleTreeHeight,
address _operator address _operator
) Mixer(_verifier, _denomination, _merkleTreeHeight, _operator) public { ) Tornado(_verifier, _denomination, _merkleTreeHeight, _operator) public {
} }
function _processDeposit() internal { function _processDeposit() internal {
@ -28,8 +28,8 @@ contract ETHMixer is Mixer {
function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal { function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal {
// sanity checks // sanity checks
require(msg.value == 0, "Message value is supposed to be zero for ETH mixer"); require(msg.value == 0, "Message value is supposed to be zero for ETH instance");
require(_refund == 0, "Refund value is supposed to be zero for ETH mixer"); require(_refund == 0, "Refund value is supposed to be zero for ETH instance");
(bool success, ) = _recipient.call.value(denomination - _fee)(""); (bool success, ) = _recipient.call.value(denomination - _fee)("");
require(success, "payment to _recipient did not go thru"); require(success, "payment to _recipient did not go thru");

View File

@ -18,7 +18,7 @@ contract IVerifier {
function verifyProof(bytes memory _proof, uint256[6] memory _input) public returns(bool); function verifyProof(bytes memory _proof, uint256[6] memory _input) public returns(bool);
} }
contract Mixer is MerkleTreeWithHistory, ReentrancyGuard { contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
uint256 public denomination; uint256 public denomination;
mapping(bytes32 => bool) public nullifierHashes; mapping(bytes32 => bool) public nullifierHashes;
// we store all commitments just to prevent accidental deposits with the same commitment // we store all commitments just to prevent accidental deposits with the same commitment
@ -56,7 +56,7 @@ contract Mixer is MerkleTreeWithHistory, ReentrancyGuard {
} }
/** /**
@dev Deposit funds into mixer. The caller must send (for ETH) or approve (for ERC20) value equal to or `denomination` of this mixer. @dev Deposit funds into the contract. The caller must send (for ETH) or approve (for ERC20) value equal to or `denomination` of this instance.
@param _commitment the note commitment, which is PedersenHash(nullifier + secret) @param _commitment the note commitment, which is PedersenHash(nullifier + secret)
*/ */
function deposit(bytes32 _commitment) external payable nonReentrant { function deposit(bytes32 _commitment) external payable nonReentrant {
@ -73,9 +73,9 @@ contract Mixer is MerkleTreeWithHistory, ReentrancyGuard {
function _processDeposit() internal; function _processDeposit() internal;
/** /**
@dev Withdraw a 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 contract. `proof` is a zkSNARK proof data, and input is an array of circuit public inputs
`input` array consists of: `input` array consists of:
- merkle root of all deposits in the mixer - merkle root of all deposits in the contract
- hash of unique deposit nullifier to prevent double spends - hash of unique deposit nullifier to prevent double spends
- the recipient of funds - the recipient of funds
- optional fee that goes to the transaction sender (usually a relay) - optional fee that goes to the transaction sender (usually a relay)

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Snark mixer test</title> <title>Tornado test</title>
</head> </head>
<body> <body>
<p> <p>

View File

@ -1,6 +1,6 @@
/* global artifacts */ /* global artifacts */
require('dotenv').config({ path: '../.env' }) require('dotenv').config({ path: '../.env' })
const ETHMixer = artifacts.require('ETHMixer') const ETHTornado = artifacts.require('ETHTornado')
const Verifier = artifacts.require('Verifier') const Verifier = artifacts.require('Verifier')
const hasherContract = artifacts.require('Hasher') const hasherContract = artifacts.require('Hasher')
@ -10,8 +10,8 @@ module.exports = function(deployer, network, accounts) {
const { MERKLE_TREE_HEIGHT, ETH_AMOUNT } = process.env const { MERKLE_TREE_HEIGHT, ETH_AMOUNT } = process.env
const verifier = await Verifier.deployed() const verifier = await Verifier.deployed()
const hasherInstance = await hasherContract.deployed() const hasherInstance = await hasherContract.deployed()
await ETHMixer.link(hasherContract, hasherInstance.address) await ETHTornado.link(hasherContract, hasherInstance.address)
const mixer = await deployer.deploy(ETHMixer, verifier.address, ETH_AMOUNT, MERKLE_TREE_HEIGHT, accounts[0]) const tornado = await deployer.deploy(ETHTornado, verifier.address, ETH_AMOUNT, MERKLE_TREE_HEIGHT, accounts[0])
console.log('ETHMixer\'s address ', mixer.address) console.log('ETHTornado\'s address ', tornado.address)
}) })
} }

View File

@ -1,6 +1,6 @@
/* global artifacts */ /* global artifacts */
require('dotenv').config({ path: '../.env' }) require('dotenv').config({ path: '../.env' })
const ERC20Mixer = artifacts.require('ERC20Mixer') const ERC20Tornado = artifacts.require('ERC20Tornado')
const Verifier = artifacts.require('Verifier') const Verifier = artifacts.require('Verifier')
const hasherContract = artifacts.require('Hasher') const hasherContract = artifacts.require('Hasher')
const ERC20Mock = artifacts.require('ERC20Mock') const ERC20Mock = artifacts.require('ERC20Mock')
@ -11,20 +11,20 @@ module.exports = function(deployer, network, accounts) {
const { MERKLE_TREE_HEIGHT, ERC20_TOKEN, TOKEN_AMOUNT } = process.env const { MERKLE_TREE_HEIGHT, ERC20_TOKEN, TOKEN_AMOUNT } = process.env
const verifier = await Verifier.deployed() const verifier = await Verifier.deployed()
const hasherInstance = await hasherContract.deployed() const hasherInstance = await hasherContract.deployed()
await ERC20Mixer.link(hasherContract, hasherInstance.address) await ERC20Tornado.link(hasherContract, hasherInstance.address)
let token = ERC20_TOKEN let token = ERC20_TOKEN
if(token === '') { if(token === '') {
const tokenInstance = await deployer.deploy(ERC20Mock) const tokenInstance = await deployer.deploy(ERC20Mock)
token = tokenInstance.address token = tokenInstance.address
} }
const mixer = await deployer.deploy( const tornado = await deployer.deploy(
ERC20Mixer, ERC20Tornado,
verifier.address, verifier.address,
TOKEN_AMOUNT, TOKEN_AMOUNT,
MERKLE_TREE_HEIGHT, MERKLE_TREE_HEIGHT,
accounts[0], accounts[0],
token, token,
) )
console.log('ERC20Mixer\'s address ', mixer.address) console.log('ERC20Tornado\'s address ', tornado.address)
}) })
} }

View File

@ -20,7 +20,7 @@
"migrate:rinkeby": "npx truffle migrate --network rinkeby --reset", "migrate:rinkeby": "npx truffle migrate --network rinkeby --reset",
"migrate:mainnet": "npx truffle migrate --network mainnet", "migrate:mainnet": "npx truffle migrate --network mainnet",
"eslint": "npx eslint --ignore-path .gitignore .", "eslint": "npx eslint --ignore-path .gitignore .",
"flat": "truffle-flattener contracts/ETHMixer.sol > ETHMixer_flat.sol contracts/ERC20Mixer.sol > ERC20Mixer_flat.sol" "flat": "npx truffle-flattener contracts/ETHTornado.sol > ETHTornado_flat.sol && npx truffle-flattener contracts/ERC20Tornado.sol > ERC20Tornado_flat.sol"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",

View File

@ -8,7 +8,7 @@ const fs = require('fs')
const { toBN } = require('web3-utils') const { toBN } = require('web3-utils')
const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper') const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper')
const Mixer = artifacts.require('./ERC20Mixer.sol') const Tornado = artifacts.require('./ERC20Tornado.sol')
const BadRecipient = artifacts.require('./BadRecipient.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')
@ -38,8 +38,8 @@ function generateDeposit() {
return deposit return deposit
} }
contract('ERC20Mixer', accounts => { contract('ERC20Tornado', accounts => {
let mixer let tornado
let token let token
let usdtToken let usdtToken
let badRecipient let badRecipient
@ -64,7 +64,7 @@ contract('ERC20Mixer', accounts => {
null, null,
prefix, prefix,
) )
mixer = await Mixer.deployed() tornado = await Tornado.deployed()
if (ERC20_TOKEN) { if (ERC20_TOKEN) {
token = await Token.at(ERC20_TOKEN) token = await Token.at(ERC20_TOKEN)
usdtToken = await USDTToken.at(ERC20_TOKEN) usdtToken = await USDTToken.at(ERC20_TOKEN)
@ -81,7 +81,7 @@ contract('ERC20Mixer', accounts => {
describe('#constructor', () => { describe('#constructor', () => {
it('should initialize', async () => { it('should initialize', async () => {
const tokenFromContract = await mixer.token() const tokenFromContract = await tornado.token()
tokenFromContract.should.be.equal(token.address) tokenFromContract.should.be.equal(token.address)
}) })
}) })
@ -89,9 +89,9 @@ contract('ERC20Mixer', accounts => {
describe('#deposit', () => { describe('#deposit', () => {
it('should work', async () => { it('should work', async () => {
const commitment = toFixedHex(43) const commitment = toFixedHex(43)
await token.approve(mixer.address, tokenDenomination) await token.approve(tornado.address, tokenDenomination)
let { logs } = await mixer.deposit(commitment, { from: sender }) let { logs } = await tornado.deposit(commitment, { from: sender })
logs[0].event.should.be.equal('Deposit') logs[0].event.should.be.equal('Deposit')
logs[0].args.commitment.should.be.equal(commitment) logs[0].args.commitment.should.be.equal(commitment)
@ -100,10 +100,10 @@ contract('ERC20Mixer', accounts => {
it('should not allow to send ether on deposit', async () => { it('should not allow to send ether on deposit', async () => {
const commitment = toFixedHex(43) const commitment = toFixedHex(43)
await token.approve(mixer.address, tokenDenomination) await token.approve(tornado.address, tokenDenomination)
let error = await mixer.deposit(commitment, { from: sender, value: 1e6 }).should.be.rejected let error = await tornado.deposit(commitment, { from: sender, value: 1e6 }).should.be.rejected
error.reason.should.be.equal('ETH value is supposed to be 0 for ERC20 mixer') error.reason.should.be.equal('ETH value is supposed to be 0 for ERC20 instance')
}) })
}) })
@ -115,11 +115,11 @@ contract('ERC20Mixer', accounts => {
await token.mint(user, tokenDenomination) await token.mint(user, tokenDenomination)
const balanceUserBefore = await token.balanceOf(user) const balanceUserBefore = await token.balanceOf(user)
await token.approve(mixer.address, tokenDenomination, { from: user }) await token.approve(tornado.address, tokenDenomination, { from: user })
// Uncomment to measure gas usage // Uncomment to measure gas usage
// let gas = await mixer.deposit.estimateGas(toBN(deposit.commitment.toString()), { from: user, gasPrice: '0' }) // let gas = await tornado.deposit.estimateGas(toBN(deposit.commitment.toString()), { from: user, gasPrice: '0' })
// console.log('deposit gas:', gas) // console.log('deposit gas:', gas)
await mixer.deposit(toFixedHex(deposit.commitment), { from: user, gasPrice: '0' }) await tornado.deposit(toFixedHex(deposit.commitment), { from: user, gasPrice: '0' })
const balanceUserAfter = await token.balanceOf(user) const balanceUserAfter = await token.balanceOf(user)
balanceUserAfter.should.be.eq.BN(toBN(balanceUserBefore).sub(toBN(tokenDenomination))) balanceUserAfter.should.be.eq.BN(toBN(balanceUserBefore).sub(toBN(tokenDenomination)))
@ -146,17 +146,17 @@ contract('ERC20Mixer', accounts => {
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
const { proof } = websnarkUtils.toSolidityInput(proofData) const { proof } = websnarkUtils.toSolidityInput(proofData)
const balanceMixerBefore = await token.balanceOf(mixer.address) const balanceTornadoBefore = await token.balanceOf(tornado.address)
const balanceRelayerBefore = await token.balanceOf(relayer) const balanceRelayerBefore = await token.balanceOf(relayer)
const balanceRecieverBefore = await token.balanceOf(toFixedHex(recipient, 20)) const balanceRecieverBefore = await token.balanceOf(toFixedHex(recipient, 20))
const ethBalanceOperatorBefore = await web3.eth.getBalance(operator) const ethBalanceOperatorBefore = await web3.eth.getBalance(operator)
const ethBalanceRecieverBefore = await web3.eth.getBalance(toFixedHex(recipient, 20)) const ethBalanceRecieverBefore = await web3.eth.getBalance(toFixedHex(recipient, 20))
const ethBalanceRelayerBefore = await web3.eth.getBalance(relayer) const ethBalanceRelayerBefore = await web3.eth.getBalance(relayer)
let isSpent = await mixer.isSpent(toFixedHex(input.nullifierHash)) let isSpent = await tornado.isSpent(toFixedHex(input.nullifierHash))
isSpent.should.be.equal(false) isSpent.should.be.equal(false)
// Uncomment to measure gas usage // Uncomment to measure gas usage
// gas = await mixer.withdraw.estimateGas(proof, publicSignals, { from: relayer, gasPrice: '0' }) // gas = await tornado.withdraw.estimateGas(proof, publicSignals, { from: relayer, gasPrice: '0' })
// console.log('withdraw gas:', gas) // console.log('withdraw gas:', gas)
const args = [ const args = [
toFixedHex(input.root), toFixedHex(input.root),
@ -166,16 +166,16 @@ contract('ERC20Mixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
const { logs } = await mixer.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' }) const { logs } = await tornado.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' })
const balanceMixerAfter = await token.balanceOf(mixer.address) const balanceTornadoAfter = await token.balanceOf(tornado.address)
const balanceRelayerAfter = await token.balanceOf(relayer) const balanceRelayerAfter = await token.balanceOf(relayer)
const ethBalanceOperatorAfter = await web3.eth.getBalance(operator) const ethBalanceOperatorAfter = await web3.eth.getBalance(operator)
const balanceRecieverAfter = await token.balanceOf(toFixedHex(recipient, 20)) const balanceRecieverAfter = await token.balanceOf(toFixedHex(recipient, 20))
const ethBalanceRecieverAfter = await web3.eth.getBalance(toFixedHex(recipient, 20)) const ethBalanceRecieverAfter = await web3.eth.getBalance(toFixedHex(recipient, 20))
const ethBalanceRelayerAfter = await web3.eth.getBalance(relayer) const ethBalanceRelayerAfter = await web3.eth.getBalance(relayer)
const feeBN = toBN(fee.toString()) const feeBN = toBN(fee.toString())
balanceMixerAfter.should.be.eq.BN(toBN(balanceMixerBefore).sub(toBN(tokenDenomination))) balanceTornadoAfter.should.be.eq.BN(toBN(balanceTornadoBefore).sub(toBN(tokenDenomination)))
balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore).add(feeBN)) balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore).add(feeBN))
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination).sub(feeBN))) balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination).sub(feeBN)))
@ -187,7 +187,7 @@ contract('ERC20Mixer', accounts => {
logs[0].args.nullifierHash.should.be.equal(toFixedHex(input.nullifierHash)) logs[0].args.nullifierHash.should.be.equal(toFixedHex(input.nullifierHash))
logs[0].args.relayer.should.be.eq.BN(relayer) logs[0].args.relayer.should.be.eq.BN(relayer)
logs[0].args.fee.should.be.eq.BN(feeBN) logs[0].args.fee.should.be.eq.BN(feeBN)
isSpent = await mixer.isSpent(toFixedHex(input.nullifierHash)) isSpent = await tornado.isSpent(toFixedHex(input.nullifierHash))
isSpent.should.be.equal(true) isSpent.should.be.equal(true)
}) })
@ -199,8 +199,8 @@ contract('ERC20Mixer', accounts => {
await token.mint(user, tokenDenomination) await token.mint(user, tokenDenomination)
const balanceUserBefore = await token.balanceOf(user) const balanceUserBefore = await token.balanceOf(user)
await token.approve(mixer.address, tokenDenomination, { from: user }) await token.approve(tornado.address, tokenDenomination, { from: user })
await mixer.deposit(toFixedHex(deposit.commitment), { from: user, gasPrice: '0' }) await tornado.deposit(toFixedHex(deposit.commitment), { from: user, gasPrice: '0' })
const balanceUserAfter = await token.balanceOf(user) const balanceUserAfter = await token.balanceOf(user)
balanceUserAfter.should.be.eq.BN(toBN(balanceUserBefore).sub(toBN(tokenDenomination))) balanceUserAfter.should.be.eq.BN(toBN(balanceUserBefore).sub(toBN(tokenDenomination)))
@ -227,14 +227,14 @@ contract('ERC20Mixer', accounts => {
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
const { proof } = websnarkUtils.toSolidityInput(proofData) const { proof } = websnarkUtils.toSolidityInput(proofData)
const balanceMixerBefore = await token.balanceOf(mixer.address) const balanceTornadoBefore = await token.balanceOf(tornado.address)
const balanceRelayerBefore = await token.balanceOf(relayer) const balanceRelayerBefore = await token.balanceOf(relayer)
const balanceRecieverBefore = await token.balanceOf(toFixedHex(recipient, 20)) const balanceRecieverBefore = await token.balanceOf(toFixedHex(recipient, 20))
const ethBalanceOperatorBefore = await web3.eth.getBalance(operator) const ethBalanceOperatorBefore = await web3.eth.getBalance(operator)
const ethBalanceRecieverBefore = await web3.eth.getBalance(toFixedHex(recipient, 20)) const ethBalanceRecieverBefore = await web3.eth.getBalance(toFixedHex(recipient, 20))
const ethBalanceRelayerBefore = await web3.eth.getBalance(relayer) const ethBalanceRelayerBefore = await web3.eth.getBalance(relayer)
let isSpent = await mixer.isSpent(toFixedHex(input.nullifierHash)) let isSpent = await tornado.isSpent(toFixedHex(input.nullifierHash))
isSpent.should.be.equal(false) isSpent.should.be.equal(false)
const args = [ const args = [
@ -245,16 +245,16 @@ contract('ERC20Mixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
const { logs } = await mixer.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' }) const { logs } = await tornado.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' })
const balanceMixerAfter = await token.balanceOf(mixer.address) const balanceTornadoAfter = await token.balanceOf(tornado.address)
const balanceRelayerAfter = await token.balanceOf(relayer) const balanceRelayerAfter = await token.balanceOf(relayer)
const ethBalanceOperatorAfter = await web3.eth.getBalance(operator) const ethBalanceOperatorAfter = await web3.eth.getBalance(operator)
const balanceRecieverAfter = await token.balanceOf(toFixedHex(recipient, 20)) const balanceRecieverAfter = await token.balanceOf(toFixedHex(recipient, 20))
const ethBalanceRecieverAfter = await web3.eth.getBalance(toFixedHex(recipient, 20)) const ethBalanceRecieverAfter = await web3.eth.getBalance(toFixedHex(recipient, 20))
const ethBalanceRelayerAfter = await web3.eth.getBalance(relayer) const ethBalanceRelayerAfter = await web3.eth.getBalance(relayer)
const feeBN = toBN(fee.toString()) const feeBN = toBN(fee.toString())
balanceMixerAfter.should.be.eq.BN(toBN(balanceMixerBefore).sub(toBN(tokenDenomination))) balanceTornadoAfter.should.be.eq.BN(toBN(balanceTornadoBefore).sub(toBN(tokenDenomination)))
balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore).add(feeBN)) balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore).add(feeBN))
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination).sub(feeBN))) balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination).sub(feeBN)))
@ -266,7 +266,7 @@ contract('ERC20Mixer', accounts => {
logs[0].args.nullifierHash.should.be.equal(toFixedHex(input.nullifierHash)) logs[0].args.nullifierHash.should.be.equal(toFixedHex(input.nullifierHash))
logs[0].args.relayer.should.be.eq.BN(relayer) logs[0].args.relayer.should.be.eq.BN(relayer)
logs[0].args.fee.should.be.eq.BN(feeBN) logs[0].args.fee.should.be.eq.BN(feeBN)
isSpent = await mixer.isSpent(toFixedHex(input.nullifierHash)) isSpent = await tornado.isSpent(toFixedHex(input.nullifierHash))
isSpent.should.be.equal(true) isSpent.should.be.equal(true)
}) })
@ -275,8 +275,8 @@ contract('ERC20Mixer', accounts => {
const user = accounts[4] const user = accounts[4]
await tree.insert(deposit.commitment) await tree.insert(deposit.commitment)
await token.mint(user, tokenDenomination) await token.mint(user, tokenDenomination)
await token.approve(mixer.address, tokenDenomination, { from: user }) await token.approve(tornado.address, tokenDenomination, { from: user })
await mixer.deposit(toFixedHex(deposit.commitment), { from: user, gasPrice: '0' }) await tornado.deposit(toFixedHex(deposit.commitment), { from: user, gasPrice: '0' })
const { root, path_elements, path_index } = await tree.path(0) const { root, path_elements, path_index } = await tree.path(0)
@ -309,11 +309,11 @@ contract('ERC20Mixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
let { reason } = await mixer.withdraw(proof, ...args, { value: 1, from: relayer, gasPrice: '0' }).should.be.rejected let { reason } = await tornado.withdraw(proof, ...args, { value: 1, from: relayer, gasPrice: '0' }).should.be.rejected
reason.should.be.equal('Incorrect refund amount received by the contract') reason.should.be.equal('Incorrect refund amount received by the contract')
;({ reason } = await mixer.withdraw(proof, ...args, { value: toBN(refund).mul(toBN(2)), from: relayer, gasPrice: '0' }).should.be.rejected) ;({ reason } = await tornado.withdraw(proof, ...args, { value: toBN(refund).mul(toBN(2)), from: relayer, gasPrice: '0' }).should.be.rejected)
reason.should.be.equal('Incorrect refund amount received by the contract') reason.should.be.equal('Incorrect refund amount received by the contract')
}) })
@ -335,11 +335,11 @@ contract('ERC20Mixer', accounts => {
const balanceUserBefore = await usdtToken.balanceOf(user) const balanceUserBefore = await usdtToken.balanceOf(user)
console.log('balanceUserBefore', balanceUserBefore.toString()) console.log('balanceUserBefore', balanceUserBefore.toString())
await usdtToken.approve(mixer.address, tokenDenomination, { from: user }) await usdtToken.approve(tornado.address, tokenDenomination, { from: user })
console.log('approve done') console.log('approve done')
const allowanceUser = await usdtToken.allowance(user, mixer.address) const allowanceUser = await usdtToken.allowance(user, tornado.address)
console.log('allowanceUser', allowanceUser.toString()) console.log('allowanceUser', allowanceUser.toString())
await mixer.deposit(toFixedHex(deposit.commitment), { from: user, gasPrice: '0' }) await tornado.deposit(toFixedHex(deposit.commitment), { from: user, gasPrice: '0' })
console.log('deposit done') console.log('deposit done')
const balanceUserAfter = await usdtToken.balanceOf(user) const balanceUserAfter = await usdtToken.balanceOf(user)
@ -368,16 +368,16 @@ contract('ERC20Mixer', accounts => {
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
const { proof } = websnarkUtils.toSolidityInput(proofData) const { proof } = websnarkUtils.toSolidityInput(proofData)
const balanceMixerBefore = await usdtToken.balanceOf(mixer.address) const balanceTornadoBefore = await usdtToken.balanceOf(tornado.address)
const balanceRelayerBefore = await usdtToken.balanceOf(relayer) const balanceRelayerBefore = await usdtToken.balanceOf(relayer)
const ethBalanceOperatorBefore = await web3.eth.getBalance(operator) const ethBalanceOperatorBefore = await web3.eth.getBalance(operator)
const balanceRecieverBefore = await usdtToken.balanceOf(toFixedHex(recipient, 20)) const balanceRecieverBefore = await usdtToken.balanceOf(toFixedHex(recipient, 20))
const ethBalanceRecieverBefore = await web3.eth.getBalance(toFixedHex(recipient, 20)) const ethBalanceRecieverBefore = await web3.eth.getBalance(toFixedHex(recipient, 20))
let isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000')) let isSpent = await tornado.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
isSpent.should.be.equal(false) isSpent.should.be.equal(false)
// Uncomment to measure gas usage // Uncomment to measure gas usage
// gas = await mixer.withdraw.estimateGas(proof, publicSignals, { from: relayer, gasPrice: '0' }) // gas = await tornado.withdraw.estimateGas(proof, publicSignals, { from: relayer, gasPrice: '0' })
// console.log('withdraw gas:', gas) // console.log('withdraw gas:', gas)
const args = [ const args = [
toFixedHex(input.root), toFixedHex(input.root),
@ -387,15 +387,15 @@ contract('ERC20Mixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
const { logs } = await mixer.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' }) const { logs } = await tornado.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' })
const balanceMixerAfter = await usdtToken.balanceOf(mixer.address) const balanceTornadoAfter = await usdtToken.balanceOf(tornado.address)
const balanceRelayerAfter = await usdtToken.balanceOf(relayer) const balanceRelayerAfter = await usdtToken.balanceOf(relayer)
const ethBalanceOperatorAfter = await web3.eth.getBalance(operator) const ethBalanceOperatorAfter = await web3.eth.getBalance(operator)
const balanceRecieverAfter = await usdtToken.balanceOf(toFixedHex(recipient, 20)) const balanceRecieverAfter = await usdtToken.balanceOf(toFixedHex(recipient, 20))
const ethBalanceRecieverAfter = await web3.eth.getBalance(toFixedHex(recipient, 20)) const ethBalanceRecieverAfter = await web3.eth.getBalance(toFixedHex(recipient, 20))
const feeBN = toBN(fee.toString()) const feeBN = toBN(fee.toString())
balanceMixerAfter.should.be.eq.BN(toBN(balanceMixerBefore).sub(toBN(tokenDenomination))) balanceTornadoAfter.should.be.eq.BN(toBN(balanceTornadoBefore).sub(toBN(tokenDenomination)))
balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore)) balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore))
ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore).add(feeBN)) ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore).add(feeBN))
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination))) balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination)))
@ -406,7 +406,7 @@ contract('ERC20Mixer', accounts => {
logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString())) logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
logs[0].args.relayer.should.be.eq.BN(operator) logs[0].args.relayer.should.be.eq.BN(operator)
logs[0].args.fee.should.be.eq.BN(feeBN) logs[0].args.fee.should.be.eq.BN(feeBN)
isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000')) isSpent = await tornado.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
isSpent.should.be.equal(true) isSpent.should.be.equal(true)
}) })
it.skip('should work with REAL DAI', async () => { it.skip('should work with REAL DAI', async () => {
@ -426,9 +426,9 @@ contract('ERC20Mixer', accounts => {
const balanceUserBefore = await token.balanceOf(user) const balanceUserBefore = await token.balanceOf(user)
console.log('balanceUserBefore', balanceUserBefore.toString()) console.log('balanceUserBefore', balanceUserBefore.toString())
await token.approve(mixer.address, tokenDenomination, { from: user }) await token.approve(tornado.address, tokenDenomination, { from: user })
console.log('approve done') console.log('approve done')
await mixer.deposit(toFixedHex(deposit.commitment), { from: user, gasPrice: '0' }) await tornado.deposit(toFixedHex(deposit.commitment), { from: user, gasPrice: '0' })
console.log('deposit done') console.log('deposit done')
const balanceUserAfter = await token.balanceOf(user) const balanceUserAfter = await token.balanceOf(user)
@ -457,16 +457,16 @@ contract('ERC20Mixer', accounts => {
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
const { proof } = websnarkUtils.toSolidityInput(proofData) const { proof } = websnarkUtils.toSolidityInput(proofData)
const balanceMixerBefore = await token.balanceOf(mixer.address) const balanceTornadoBefore = await token.balanceOf(tornado.address)
const balanceRelayerBefore = await token.balanceOf(relayer) const balanceRelayerBefore = await token.balanceOf(relayer)
const ethBalanceOperatorBefore = await web3.eth.getBalance(operator) const ethBalanceOperatorBefore = await web3.eth.getBalance(operator)
const balanceRecieverBefore = await token.balanceOf(toFixedHex(recipient, 20)) const balanceRecieverBefore = await token.balanceOf(toFixedHex(recipient, 20))
const ethBalanceRecieverBefore = await web3.eth.getBalance(toFixedHex(recipient, 20)) const ethBalanceRecieverBefore = await web3.eth.getBalance(toFixedHex(recipient, 20))
let isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000')) let isSpent = await tornado.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
isSpent.should.be.equal(false) isSpent.should.be.equal(false)
// Uncomment to measure gas usage // Uncomment to measure gas usage
// gas = await mixer.withdraw.estimateGas(proof, publicSignals, { from: relayer, gasPrice: '0' }) // gas = await tornado.withdraw.estimateGas(proof, publicSignals, { from: relayer, gasPrice: '0' })
// console.log('withdraw gas:', gas) // console.log('withdraw gas:', gas)
const args = [ const args = [
toFixedHex(input.root), toFixedHex(input.root),
@ -476,16 +476,16 @@ contract('ERC20Mixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
const { logs } = await mixer.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' }) const { logs } = await tornado.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' })
console.log('withdraw done') console.log('withdraw done')
const balanceMixerAfter = await token.balanceOf(mixer.address) const balanceTornadoAfter = await token.balanceOf(tornado.address)
const balanceRelayerAfter = await token.balanceOf(relayer) const balanceRelayerAfter = await token.balanceOf(relayer)
const ethBalanceOperatorAfter = await web3.eth.getBalance(operator) const ethBalanceOperatorAfter = await web3.eth.getBalance(operator)
const balanceRecieverAfter = await token.balanceOf(toFixedHex(recipient, 20)) const balanceRecieverAfter = await token.balanceOf(toFixedHex(recipient, 20))
const ethBalanceRecieverAfter = await web3.eth.getBalance(toFixedHex(recipient, 20)) const ethBalanceRecieverAfter = await web3.eth.getBalance(toFixedHex(recipient, 20))
const feeBN = toBN(fee.toString()) const feeBN = toBN(fee.toString())
balanceMixerAfter.should.be.eq.BN(toBN(balanceMixerBefore).sub(toBN(tokenDenomination))) balanceTornadoAfter.should.be.eq.BN(toBN(balanceTornadoBefore).sub(toBN(tokenDenomination)))
balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore)) balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore))
ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore).add(feeBN)) ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore).add(feeBN))
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination))) balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination)))
@ -496,7 +496,7 @@ contract('ERC20Mixer', accounts => {
logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString())) logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
logs[0].args.relayer.should.be.eq.BN(operator) logs[0].args.relayer.should.be.eq.BN(operator)
logs[0].args.fee.should.be.eq.BN(feeBN) logs[0].args.fee.should.be.eq.BN(feeBN)
isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000')) isSpent = await tornado.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
isSpent.should.be.equal(true) isSpent.should.be.equal(true)
}) })
}) })

View File

@ -8,7 +8,7 @@ const fs = require('fs')
const { toBN, randomHex } = require('web3-utils') const { toBN, randomHex } = require('web3-utils')
const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper') const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper')
const Mixer = artifacts.require('./ETHMixer.sol') const Tornado = artifacts.require('./ETHTornado.sol')
const { ETH_AMOUNT, MERKLE_TREE_HEIGHT } = process.env const { ETH_AMOUNT, MERKLE_TREE_HEIGHT } = process.env
const websnarkUtils = require('websnark/src/utils') const websnarkUtils = require('websnark/src/utils')
@ -51,8 +51,8 @@ function snarkVerify(proof) {
return snarkjs['groth'].isValid(verification_key, proof, proof.publicSignals) return snarkjs['groth'].isValid(verification_key, proof, proof.publicSignals)
} }
contract('ETHMixer', accounts => { contract('ETHTornado', accounts => {
let mixer let tornado
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
@ -74,7 +74,7 @@ contract('ETHMixer', accounts => {
null, null,
prefix, prefix,
) )
mixer = await Mixer.deployed() tornado = await Tornado.deployed()
snapshotId = await takeSnapshot() snapshotId = await takeSnapshot()
groth16 = await buildGroth16() groth16 = await buildGroth16()
circuit = require('../build/circuits/withdraw.json') circuit = require('../build/circuits/withdraw.json')
@ -83,7 +83,7 @@ contract('ETHMixer', accounts => {
describe('#constructor', () => { describe('#constructor', () => {
it('should initialize', async () => { it('should initialize', async () => {
const etherDenomination = await mixer.denomination() const etherDenomination = await tornado.denomination()
etherDenomination.should.be.eq.BN(toBN(value)) etherDenomination.should.be.eq.BN(toBN(value))
}) })
}) })
@ -91,14 +91,14 @@ contract('ETHMixer', accounts => {
describe('#deposit', () => { describe('#deposit', () => {
it('should emit event', async () => { it('should emit event', async () => {
let commitment = toFixedHex(42) let commitment = toFixedHex(42)
let { logs } = await mixer.deposit(commitment, { value, from: sender }) let { logs } = await tornado.deposit(commitment, { value, from: sender })
logs[0].event.should.be.equal('Deposit') logs[0].event.should.be.equal('Deposit')
logs[0].args.commitment.should.be.equal(commitment) logs[0].args.commitment.should.be.equal(commitment)
logs[0].args.leafIndex.should.be.eq.BN(0) logs[0].args.leafIndex.should.be.eq.BN(0)
commitment = toFixedHex(12); commitment = toFixedHex(12);
({ logs } = await mixer.deposit(commitment, { value, from: accounts[2] })) ({ logs } = await tornado.deposit(commitment, { value, from: accounts[2] }))
logs[0].event.should.be.equal('Deposit') logs[0].event.should.be.equal('Deposit')
logs[0].args.commitment.should.be.equal(commitment) logs[0].args.commitment.should.be.equal(commitment)
@ -107,8 +107,8 @@ contract('ETHMixer', accounts => {
it('should throw if there is a such commitment', async () => { it('should throw if there is a such commitment', async () => {
const commitment = toFixedHex(42) const commitment = toFixedHex(42)
await mixer.deposit(commitment, { value, from: sender }).should.be.fulfilled await tornado.deposit(commitment, { value, from: sender }).should.be.fulfilled
const error = await mixer.deposit(commitment, { value, from: sender }).should.be.rejected const error = await tornado.deposit(commitment, { value, from: sender }).should.be.rejected
error.reason.should.be.equal('The commitment has been submitted') error.reason.should.be.equal('The commitment has been submitted')
}) })
}) })
@ -166,9 +166,9 @@ contract('ETHMixer', accounts => {
const balanceUserBefore = await web3.eth.getBalance(user) const balanceUserBefore = await web3.eth.getBalance(user)
// Uncomment to measure gas usage // Uncomment to measure gas usage
// let gas = await mixer.deposit.estimateGas(toBN(deposit.commitment.toString()), { value, from: user, gasPrice: '0' }) // let gas = await tornado.deposit.estimateGas(toBN(deposit.commitment.toString()), { value, from: user, gasPrice: '0' })
// console.log('deposit gas:', gas) // console.log('deposit gas:', gas)
await mixer.deposit(toFixedHex(deposit.commitment), { value, from: user, gasPrice: '0' }) await tornado.deposit(toFixedHex(deposit.commitment), { value, from: user, gasPrice: '0' })
const balanceUserAfter = await web3.eth.getBalance(user) const balanceUserAfter = await web3.eth.getBalance(user)
balanceUserAfter.should.be.eq.BN(toBN(balanceUserBefore).sub(toBN(value))) balanceUserAfter.should.be.eq.BN(toBN(balanceUserBefore).sub(toBN(value)))
@ -196,15 +196,15 @@ contract('ETHMixer', accounts => {
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
const { proof } = websnarkUtils.toSolidityInput(proofData) const { proof } = websnarkUtils.toSolidityInput(proofData)
const balanceMixerBefore = await web3.eth.getBalance(mixer.address) const balanceTornadoBefore = await web3.eth.getBalance(tornado.address)
const balanceRelayerBefore = await web3.eth.getBalance(relayer) const balanceRelayerBefore = await web3.eth.getBalance(relayer)
const balanceOperatorBefore = await web3.eth.getBalance(operator) const balanceOperatorBefore = await web3.eth.getBalance(operator)
const balanceRecieverBefore = await web3.eth.getBalance(toFixedHex(recipient, 20)) const balanceRecieverBefore = await web3.eth.getBalance(toFixedHex(recipient, 20))
let isSpent = await mixer.isSpent(toFixedHex(input.nullifierHash)) let isSpent = await tornado.isSpent(toFixedHex(input.nullifierHash))
isSpent.should.be.equal(false) isSpent.should.be.equal(false)
// Uncomment to measure gas usage // Uncomment to measure gas usage
// gas = await mixer.withdraw.estimateGas(proof, publicSignals, { from: relayer, gasPrice: '0' }) // gas = await tornado.withdraw.estimateGas(proof, publicSignals, { from: relayer, gasPrice: '0' })
// console.log('withdraw gas:', gas) // console.log('withdraw gas:', gas)
const args = [ const args = [
toFixedHex(input.root), toFixedHex(input.root),
@ -214,14 +214,14 @@ contract('ETHMixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
const { logs } = await mixer.withdraw(proof, ...args, { from: relayer, gasPrice: '0' }) const { logs } = await tornado.withdraw(proof, ...args, { from: relayer, gasPrice: '0' })
const balanceMixerAfter = await web3.eth.getBalance(mixer.address) const balanceTornadoAfter = await web3.eth.getBalance(tornado.address)
const balanceRelayerAfter = await web3.eth.getBalance(relayer) const balanceRelayerAfter = await web3.eth.getBalance(relayer)
const balanceOperatorAfter = await web3.eth.getBalance(operator) const balanceOperatorAfter = await web3.eth.getBalance(operator)
const balanceRecieverAfter = await web3.eth.getBalance(toFixedHex(recipient, 20)) const balanceRecieverAfter = await web3.eth.getBalance(toFixedHex(recipient, 20))
const feeBN = toBN(fee.toString()) const feeBN = toBN(fee.toString())
balanceMixerAfter.should.be.eq.BN(toBN(balanceMixerBefore).sub(toBN(value))) balanceTornadoAfter.should.be.eq.BN(toBN(balanceTornadoBefore).sub(toBN(value)))
balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore)) balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore))
balanceOperatorAfter.should.be.eq.BN(toBN(balanceOperatorBefore).add(feeBN)) balanceOperatorAfter.should.be.eq.BN(toBN(balanceOperatorBefore).add(feeBN))
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(value)).sub(feeBN)) balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(value)).sub(feeBN))
@ -231,14 +231,14 @@ contract('ETHMixer', accounts => {
logs[0].args.nullifierHash.should.be.equal(toFixedHex(input.nullifierHash)) logs[0].args.nullifierHash.should.be.equal(toFixedHex(input.nullifierHash))
logs[0].args.relayer.should.be.eq.BN(operator) logs[0].args.relayer.should.be.eq.BN(operator)
logs[0].args.fee.should.be.eq.BN(feeBN) logs[0].args.fee.should.be.eq.BN(feeBN)
isSpent = await mixer.isSpent(toFixedHex(input.nullifierHash)) isSpent = await tornado.isSpent(toFixedHex(input.nullifierHash))
isSpent.should.be.equal(true) isSpent.should.be.equal(true)
}) })
it('should prevent double spend', async () => { it('should prevent double spend', async () => {
const deposit = generateDeposit() const deposit = generateDeposit()
await tree.insert(deposit.commitment) await tree.insert(deposit.commitment)
await mixer.deposit(toFixedHex(deposit.commitment), { value, from: sender }) await tornado.deposit(toFixedHex(deposit.commitment), { value, from: sender })
const { root, path_elements, path_index } = await tree.path(0) const { root, path_elements, path_index } = await tree.path(0)
@ -264,15 +264,15 @@ contract('ETHMixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
await mixer.withdraw(proof, ...args, { from: relayer }).should.be.fulfilled await tornado.withdraw(proof, ...args, { from: relayer }).should.be.fulfilled
const error = await mixer.withdraw(proof, ...args, { from: relayer }).should.be.rejected const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
error.reason.should.be.equal('The note has been already spent') error.reason.should.be.equal('The note has been already spent')
}) })
it('should prevent double spend with overflow', async () => { it('should prevent double spend with overflow', async () => {
const deposit = generateDeposit() const deposit = generateDeposit()
await tree.insert(deposit.commitment) await tree.insert(deposit.commitment)
await mixer.deposit(toFixedHex(deposit.commitment), { value, from: sender }) await tornado.deposit(toFixedHex(deposit.commitment), { value, from: sender })
const { root, path_elements, path_index } = await tree.path(0) const { root, path_elements, path_index } = await tree.path(0)
@ -298,14 +298,14 @@ contract('ETHMixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
const error = await mixer.withdraw(proof, ...args, { from: relayer }).should.be.rejected const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
error.reason.should.be.equal('verifier-gte-snark-scalar-field') error.reason.should.be.equal('verifier-gte-snark-scalar-field')
}) })
it('fee should be less or equal transfer value', async () => { it('fee should be less or equal transfer value', async () => {
const deposit = generateDeposit() const deposit = generateDeposit()
await tree.insert(deposit.commitment) await tree.insert(deposit.commitment)
await mixer.deposit(toFixedHex(deposit.commitment), { value, from: sender }) await tornado.deposit(toFixedHex(deposit.commitment), { value, from: sender })
const { root, path_elements, path_index } = await tree.path(0) const { root, path_elements, path_index } = await tree.path(0)
const largeFee = bigInt(value).add(bigInt(1)) const largeFee = bigInt(value).add(bigInt(1))
@ -332,14 +332,14 @@ contract('ETHMixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
const error = await mixer.withdraw(proof, ...args, { from: relayer }).should.be.rejected const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
error.reason.should.be.equal('Fee exceeds transfer value') error.reason.should.be.equal('Fee exceeds transfer value')
}) })
it('should throw for corrupted merkle tree root', async () => { it('should throw for corrupted merkle tree root', async () => {
const deposit = generateDeposit() const deposit = generateDeposit()
await tree.insert(deposit.commitment) await tree.insert(deposit.commitment)
await mixer.deposit(toFixedHex(deposit.commitment), { value, from: sender }) await tornado.deposit(toFixedHex(deposit.commitment), { value, from: sender })
const { root, path_elements, path_index } = await tree.path(0) const { root, path_elements, path_index } = await tree.path(0)
@ -367,14 +367,14 @@ contract('ETHMixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
const error = await mixer.withdraw(proof, ...args, { from: relayer }).should.be.rejected const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
error.reason.should.be.equal('Cannot find your merkle root') error.reason.should.be.equal('Cannot find your merkle root')
}) })
it('should reject with tampered public inputs', async () => { it('should reject with tampered public inputs', async () => {
const deposit = generateDeposit() const deposit = generateDeposit()
await tree.insert(deposit.commitment) await tree.insert(deposit.commitment)
await mixer.deposit(toFixedHex(deposit.commitment), { value, from: sender }) await tornado.deposit(toFixedHex(deposit.commitment), { value, from: sender })
let { root, path_elements, path_index } = await tree.path(0) let { root, path_elements, path_index } = await tree.path(0)
@ -412,7 +412,7 @@ contract('ETHMixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
let error = await mixer.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected let error = await tornado.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected
error.reason.should.be.equal('Invalid withdraw proof') error.reason.should.be.equal('Invalid withdraw proof')
// fee // fee
@ -424,7 +424,7 @@ contract('ETHMixer', accounts => {
toFixedHex('0x000000000000000000000000000000000000000000000000015345785d8a0000'), toFixedHex('0x000000000000000000000000000000000000000000000000015345785d8a0000'),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
error = await mixer.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected error = await tornado.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected
error.reason.should.be.equal('Invalid withdraw proof') error.reason.should.be.equal('Invalid withdraw proof')
// nullifier // nullifier
@ -436,21 +436,21 @@ contract('ETHMixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
error = await mixer.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected error = await tornado.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected
error.reason.should.be.equal('Invalid withdraw proof') error.reason.should.be.equal('Invalid withdraw proof')
// proof itself // proof itself
proof = '0xbeef' + proof.substr(6) proof = '0xbeef' + proof.substr(6)
await mixer.withdraw(proof, ...args, { from: relayer }).should.be.rejected await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
// should work with original values // should work with original values
await mixer.withdraw(originalProof, ...args, { from: relayer }).should.be.fulfilled await tornado.withdraw(originalProof, ...args, { from: relayer }).should.be.fulfilled
}) })
it('should reject with non zero refund', async () => { it('should reject with non zero refund', async () => {
const deposit = generateDeposit() const deposit = generateDeposit()
await tree.insert(deposit.commitment) await tree.insert(deposit.commitment)
await mixer.deposit(toFixedHex(deposit.commitment), { value, from: sender }) await tornado.deposit(toFixedHex(deposit.commitment), { value, from: sender })
const { root, path_elements, path_index } = await tree.path(0) const { root, path_elements, path_index } = await tree.path(0)
@ -478,29 +478,29 @@ contract('ETHMixer', accounts => {
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
const error = await mixer.withdraw(proof, ...args, { from: relayer }).should.be.rejected const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
error.reason.should.be.equal('Refund value is supposed to be zero for ETH mixer') error.reason.should.be.equal('Refund value is supposed to be zero for ETH instance')
}) })
}) })
describe('#changeOperator', () => { describe('#changeOperator', () => {
it('should work', async () => { it('should work', async () => {
let operator = await mixer.operator() let operator = await tornado.operator()
operator.should.be.equal(sender) operator.should.be.equal(sender)
const newOperator = accounts[7] const newOperator = accounts[7]
await mixer.changeOperator(newOperator).should.be.fulfilled await tornado.changeOperator(newOperator).should.be.fulfilled
operator = await mixer.operator() operator = await tornado.operator()
operator.should.be.equal(newOperator) operator.should.be.equal(newOperator)
}) })
it('cannot change from different address', async () => { it('cannot change from different address', async () => {
let operator = await mixer.operator() let operator = await tornado.operator()
operator.should.be.equal(sender) operator.should.be.equal(sender)
const newOperator = accounts[7] const newOperator = accounts[7]
const error = await mixer.changeOperator(newOperator, { from: accounts[7] }).should.be.rejected const error = await tornado.changeOperator(newOperator, { from: accounts[7] }).should.be.rejected
error.reason.should.be.equal('Only operator can call this function.') error.reason.should.be.equal('Only operator can call this function.')
}) })
@ -508,22 +508,22 @@ contract('ETHMixer', accounts => {
describe('#updateVerifier', () => { describe('#updateVerifier', () => {
it('should work', async () => { it('should work', async () => {
let operator = await mixer.operator() let operator = await tornado.operator()
operator.should.be.equal(sender) operator.should.be.equal(sender)
const newVerifier = accounts[7] const newVerifier = accounts[7]
await mixer.updateVerifier(newVerifier).should.be.fulfilled await tornado.updateVerifier(newVerifier).should.be.fulfilled
const verifier = await mixer.verifier() const verifier = await tornado.verifier()
verifier.should.be.equal(newVerifier) verifier.should.be.equal(newVerifier)
}) })
it('cannot change from different address', async () => { it('cannot change from different address', async () => {
let operator = await mixer.operator() let operator = await tornado.operator()
operator.should.be.equal(sender) operator.should.be.equal(sender)
const newVerifier = accounts[7] const newVerifier = accounts[7]
const error = await mixer.updateVerifier(newVerifier, { from: accounts[7] }).should.be.rejected const error = await tornado.updateVerifier(newVerifier, { from: accounts[7] }).should.be.rejected
error.reason.should.be.equal('Only operator can call this function.') error.reason.should.be.equal('Only operator can call this function.')
}) })
@ -535,8 +535,8 @@ contract('ETHMixer', accounts => {
const deposit2 = generateDeposit() const deposit2 = generateDeposit()
await tree.insert(deposit1.commitment) await tree.insert(deposit1.commitment)
await tree.insert(deposit2.commitment) await tree.insert(deposit2.commitment)
await mixer.deposit(toFixedHex(deposit1.commitment), { value, gasPrice: '0' }) await tornado.deposit(toFixedHex(deposit1.commitment), { value, gasPrice: '0' })
await mixer.deposit(toFixedHex(deposit2.commitment), { value, gasPrice: '0' }) await tornado.deposit(toFixedHex(deposit2.commitment), { value, gasPrice: '0' })
const { root, path_elements, path_index } = await tree.path(1) const { root, path_elements, path_index } = await tree.path(1)
@ -570,11 +570,11 @@ contract('ETHMixer', accounts => {
toFixedHex(input.refund) toFixedHex(input.refund)
] ]
await mixer.withdraw(proof, ...args, { from: relayer, gasPrice: '0' }) await tornado.withdraw(proof, ...args, { from: relayer, gasPrice: '0' })
const nullifierHash1 = toFixedHex(pedersenHash(deposit1.nullifier.leInt2Buff(31))) const nullifierHash1 = toFixedHex(pedersenHash(deposit1.nullifier.leInt2Buff(31)))
const nullifierHash2 = toFixedHex(pedersenHash(deposit2.nullifier.leInt2Buff(31))) const nullifierHash2 = toFixedHex(pedersenHash(deposit2.nullifier.leInt2Buff(31)))
const spentArray = await mixer.isSpentArray([nullifierHash1, nullifierHash2]) const spentArray = await tornado.isSpentArray([nullifierHash1, nullifierHash2])
spentArray.should.be.deep.equal([false, true]) spentArray.should.be.deep.equal([false, true])
}) })
}) })