This commit is contained in:
poma 2021-02-11 09:23:18 +03:00
parent c6b442713a
commit 346ffcee3c
No known key found for this signature in database
GPG Key ID: BA20CB01FE165657
35 changed files with 852 additions and 8943 deletions

27
.eslintrc Normal file
View File

@ -0,0 +1,27 @@
{
"env": {
"node": true,
"browser": true,
"es6": true,
"mocha": true
},
"extends": ["eslint:recommended", "plugin:prettier/recommended", "prettier"],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"indent": ["error", 2],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "single"],
"semi": ["error", "never"],
"object-curly-spacing": ["error", "always"],
"comma-dangle": ["error", "always-multiline"],
"require-await": "error",
"prettier/prettier": ["error", { "printWidth": 110 }]
}
}

View File

@ -1,39 +0,0 @@
{
"env": {
"node": true,
"browser": true,
"es6": true,
"mocha": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"indent": [
"error",
2
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"never"
],
"object-curly-spacing": [
"error",
"always"
],
"require-await": "error"
}
}

2
.nvmrc
View File

@ -1 +1 @@
11 12

6
.prettierignore Normal file
View File

@ -0,0 +1,6 @@
.vscode
build
circuits
contracts/Verifier.sol
lib/ganacheHelper.js
cli.js

16
.prettierrc Normal file
View File

@ -0,0 +1,16 @@
{
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"semi": false,
"printWidth": 110,
"overrides": [
{
"files": "*.sol",
"options": {
"singleQuote": false,
"printWidth": 130
}
}
]
}

View File

@ -1,6 +1,13 @@
{ {
"extends": "solhint:recommended", "extends": "solhint:recommended",
"rules": { "rules": {
"indent": ["error", 2] "prettier/prettier": [
"error",
{
"printWidth": 110
} }
],
"quotes": ["error", "double"]
},
"plugins": ["prettier"]
} }

View File

@ -1,7 +1,7 @@
dist: trusty dist: trusty
language: node_js language: node_js
node_js: node_js:
- "11" - '11'
install: install:
- npm ci - npm ci
- cp .env.example .env - cp .env.example .env

16
cli.js
View File

@ -178,7 +178,7 @@ async function generateProof({ deposit, recipient, relayerAddress = 0, fee = 0,
toHex(input.recipient, 20), toHex(input.recipient, 20),
toHex(input.relayer, 20), toHex(input.relayer, 20),
toHex(input.fee), toHex(input.fee),
toHex(input.refund) toHex(input.refund),
] ]
return { proof, args } return { proof, args }
@ -265,7 +265,7 @@ function fromDecimals({ amount, decimals }) {
const comps = ether.split('.') const comps = ether.split('.')
if (comps.length > 2) { if (comps.length > 2) {
throw new Error( throw new Error(
'[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal points' '[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal points',
) )
} }
@ -280,7 +280,7 @@ function fromDecimals({ amount, decimals }) {
} }
if (fraction.length > baseLength) { if (fraction.length > baseLength) {
throw new Error( throw new Error(
'[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal places' '[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal places',
) )
} }
@ -417,10 +417,10 @@ async function loadDepositData({ deposit }) {
try { try {
const eventWhenHappened = await tornado.getPastEvents('Deposit', { const eventWhenHappened = await tornado.getPastEvents('Deposit', {
filter: { filter: {
commitment: deposit.commitmentHex commitment: deposit.commitmentHex,
}, },
fromBlock: 0, fromBlock: 0,
toBlock: 'latest' toBlock: 'latest',
}) })
if (eventWhenHappened.length === 0) { if (eventWhenHappened.length === 0) {
throw new Error('There is no related deposit, the note is invalid') throw new Error('There is no related deposit, the note is invalid')
@ -441,7 +441,7 @@ async function loadWithdrawalData({ amount, currency, deposit }) {
try { try {
const events = await await tornado.getPastEvents('Withdrawal', { const events = await await tornado.getPastEvents('Withdrawal', {
fromBlock: 0, fromBlock: 0,
toBlock: 'latest' toBlock: 'latest',
}) })
const withdrawEvent = events.filter((event) => { const withdrawEvent = events.filter((event) => {
@ -451,7 +451,7 @@ async function loadWithdrawalData({ amount, currency, deposit }) {
const fee = withdrawEvent.returnValues.fee const fee = withdrawEvent.returnValues.fee
const decimals = config.deployments[`netId${netId}`][currency].decimals const decimals = config.deployments[`netId${netId}`][currency].decimals
const withdrawalAmount = toBN(fromDecimals({ amount, decimals })).sub( const withdrawalAmount = toBN(fromDecimals({ amount, decimals })).sub(
toBN(fee) toBN(fee),
) )
const { timestamp } = await web3.eth.getBlock(withdrawEvent.blockHash) const { timestamp } = await web3.eth.getBlock(withdrawEvent.blockHash)
return { return {
@ -460,7 +460,7 @@ async function loadWithdrawalData({ amount, currency, deposit }) {
to: withdrawEvent.returnValues.to, to: withdrawEvent.returnValues.to,
timestamp, timestamp,
nullifier: deposit.nullifierHex, nullifier: deposit.nullifierHex,
fee: toDecimals(fee, decimals, 9) fee: toDecimals(fee, decimals, 9),
} }
} catch (e) { } catch (e) {
console.error('loadWithdrawalData', e) console.error('loadWithdrawalData', e)

View File

@ -8,11 +8,11 @@ const genContract = require('circomlib/src/mimcsponge_gencontract.js')
// command // command
const outputPath = path.join(__dirname, 'build', 'Hasher.json') const outputPath = path.join(__dirname, 'build', 'Hasher.json')
function main () { function main() {
const contract = { const contract = {
contractName: 'Hasher', contractName: 'Hasher',
abi: genContract.abi, abi: genContract.abi,
bytecode: genContract.createCode('mimcsponge', 220) bytecode: genContract.createCode('mimcsponge', 220),
} }
fs.writeFileSync(outputPath, JSON.stringify(contract)) fs.writeFileSync(outputPath, JSON.stringify(contract))

128
config.js
View File

@ -5,136 +5,136 @@ module.exports = {
netId1: { netId1: {
eth: { eth: {
instanceAddress: { instanceAddress: {
'0.1': '0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc', 0.1: '0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc',
'1': '0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936', 1: '0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936',
'10': '0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF', 10: '0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF',
'100': '0xA160cdAB225685dA1d56aa342Ad8841c3b53f291' 100: '0xA160cdAB225685dA1d56aa342Ad8841c3b53f291',
}, },
symbol: 'ETH', symbol: 'ETH',
decimals: 18 decimals: 18,
}, },
dai: { dai: {
instanceAddress: { instanceAddress: {
'100': '0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3', 100: '0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3',
'1000': '0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144', 1000: '0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144',
'10000': '0xF60dD140cFf0706bAE9Cd734Ac3ae76AD9eBC32A', 10000: '0xF60dD140cFf0706bAE9Cd734Ac3ae76AD9eBC32A',
'100000': undefined 100000: undefined,
}, },
tokenAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F', tokenAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
symbol: 'DAI', symbol: 'DAI',
decimals: 18 decimals: 18,
}, },
cdai: { cdai: {
instanceAddress: { instanceAddress: {
'5000': '0x22aaA7720ddd5388A3c0A3333430953C68f1849b', 5000: '0x22aaA7720ddd5388A3c0A3333430953C68f1849b',
'50000': '0xBA214C1c1928a32Bffe790263E38B4Af9bFCD659', 50000: '0xBA214C1c1928a32Bffe790263E38B4Af9bFCD659',
'500000': '0xb1C8094B234DcE6e03f10a5b673c1d8C69739A00', 500000: '0xb1C8094B234DcE6e03f10a5b673c1d8C69739A00',
'5000000': undefined 5000000: undefined,
}, },
tokenAddress: '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643', tokenAddress: '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643',
symbol: 'cDAI', symbol: 'cDAI',
decimals: 8 decimals: 8,
}, },
usdc: { usdc: {
instanceAddress: { instanceAddress: {
'100': '0xd96f2B1c14Db8458374d9Aca76E26c3D18364307', 100: '0xd96f2B1c14Db8458374d9Aca76E26c3D18364307',
'1000': '0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D', 1000: '0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D',
'10000': '0xD691F27f38B395864Ea86CfC7253969B409c362d', 10000: '0xD691F27f38B395864Ea86CfC7253969B409c362d',
'100000': undefined 100000: undefined,
}, },
tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
symbol: 'USDC', symbol: 'USDC',
decimals: 6 decimals: 6,
}, },
cusdc: { cusdc: {
instanceAddress: { instanceAddress: {
'5000': '0xaEaaC358560e11f52454D997AAFF2c5731B6f8a6', 5000: '0xaEaaC358560e11f52454D997AAFF2c5731B6f8a6',
'50000': '0x1356c899D8C9467C7f71C195612F8A395aBf2f0a', 50000: '0x1356c899D8C9467C7f71C195612F8A395aBf2f0a',
'500000': '0xA60C772958a3eD56c1F15dD055bA37AC8e523a0D', 500000: '0xA60C772958a3eD56c1F15dD055bA37AC8e523a0D',
'5000000': undefined 5000000: undefined,
}, },
tokenAddress: '0x39AA39c021dfbaE8faC545936693aC917d5E7563', tokenAddress: '0x39AA39c021dfbaE8faC545936693aC917d5E7563',
symbol: 'cUSDC', symbol: 'cUSDC',
decimals: 8 decimals: 8,
}, },
usdt: { usdt: {
instanceAddress: { instanceAddress: {
'100': '0x169AD27A470D064DEDE56a2D3ff727986b15D52B', 100: '0x169AD27A470D064DEDE56a2D3ff727986b15D52B',
'1000': '0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f', 1000: '0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f',
'10000': '0xF67721A2D8F736E75a49FdD7FAd2e31D8676542a', 10000: '0xF67721A2D8F736E75a49FdD7FAd2e31D8676542a',
'100000': '0x9AD122c22B14202B4490eDAf288FDb3C7cb3ff5E' 100000: '0x9AD122c22B14202B4490eDAf288FDb3C7cb3ff5E',
}, },
tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
symbol: 'USDT', symbol: 'USDT',
decimals: 6 decimals: 6,
} },
}, },
netId42: { netId42: {
eth: { eth: {
instanceAddress: { instanceAddress: {
'0.1': '0x8b3f5393bA08c24cc7ff5A66a832562aAB7bC95f', 0.1: '0x8b3f5393bA08c24cc7ff5A66a832562aAB7bC95f',
'1': '0xD6a6AC46d02253c938B96D12BE439F570227aE8E', 1: '0xD6a6AC46d02253c938B96D12BE439F570227aE8E',
'10': '0xe1BE96331391E519471100c3c1528B66B8F4e5a7', 10: '0xe1BE96331391E519471100c3c1528B66B8F4e5a7',
'100': '0xd037E0Ac98Dab2fCb7E296c69C6e52767Ae5414D' 100: '0xd037E0Ac98Dab2fCb7E296c69C6e52767Ae5414D',
}, },
symbol: 'ETH', symbol: 'ETH',
decimals: 18 decimals: 18,
}, },
dai: { dai: {
instanceAddress: { instanceAddress: {
'100': '0xdf2d3cC5F361CF95b3f62c4bB66deFe3FDE47e3D', 100: '0xdf2d3cC5F361CF95b3f62c4bB66deFe3FDE47e3D',
'1000': '0xD96291dFa35d180a71964D0894a1Ae54247C4ccD', 1000: '0xD96291dFa35d180a71964D0894a1Ae54247C4ccD',
'10000': '0xb192794f72EA45e33C3DF6fe212B9c18f6F45AE3', 10000: '0xb192794f72EA45e33C3DF6fe212B9c18f6F45AE3',
'100000': undefined 100000: undefined,
}, },
tokenAddress: '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa', tokenAddress: '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa',
symbol: 'DAI', symbol: 'DAI',
decimals: 18 decimals: 18,
}, },
cdai: { cdai: {
instanceAddress: { instanceAddress: {
'5000': '0x6Fc9386ABAf83147b3a89C36D422c625F44121C8', 5000: '0x6Fc9386ABAf83147b3a89C36D422c625F44121C8',
'50000': '0x7182EA067e0f050997444FCb065985Fd677C16b6', 50000: '0x7182EA067e0f050997444FCb065985Fd677C16b6',
'500000': '0xC22ceFd90fbd1FdEeE554AE6Cc671179BC3b10Ae', 500000: '0xC22ceFd90fbd1FdEeE554AE6Cc671179BC3b10Ae',
'5000000': undefined 5000000: undefined,
}, },
tokenAddress: '0xe7bc397DBd069fC7d0109C0636d06888bb50668c', tokenAddress: '0xe7bc397DBd069fC7d0109C0636d06888bb50668c',
symbol: 'cDAI', symbol: 'cDAI',
decimals: 8 decimals: 8,
}, },
usdc: { usdc: {
instanceAddress: { instanceAddress: {
'100': '0x137E2B6d185018e7f09f6cf175a970e7fC73826C', 100: '0x137E2B6d185018e7f09f6cf175a970e7fC73826C',
'1000': '0xcC7f1633A5068E86E3830e692e3e3f8f520525Af', 1000: '0xcC7f1633A5068E86E3830e692e3e3f8f520525Af',
'10000': '0x28C8f149a0ab8A9bdB006B8F984fFFCCE52ef5EF', 10000: '0x28C8f149a0ab8A9bdB006B8F984fFFCCE52ef5EF',
'100000': undefined 100000: undefined,
}, },
tokenAddress: '0x75B0622Cec14130172EaE9Cf166B92E5C112FaFF', tokenAddress: '0x75B0622Cec14130172EaE9Cf166B92E5C112FaFF',
symbol: 'USDC', symbol: 'USDC',
decimals: 6 decimals: 6,
}, },
cusdc: { cusdc: {
instanceAddress: { instanceAddress: {
'5000': '0xc0648F28ABA385c8a1421Bbf1B59e3c474F89cB0', 5000: '0xc0648F28ABA385c8a1421Bbf1B59e3c474F89cB0',
'50000': '0x0C53853379c6b1A7B74E0A324AcbDD5Eabd4981D', 50000: '0x0C53853379c6b1A7B74E0A324AcbDD5Eabd4981D',
'500000': '0xf84016A0E03917cBe700D318EB1b7a53e6e3dEe1', 500000: '0xf84016A0E03917cBe700D318EB1b7a53e6e3dEe1',
'5000000': undefined 5000000: undefined,
}, },
tokenAddress: '0xcfC9bB230F00bFFDB560fCe2428b4E05F3442E35', tokenAddress: '0xcfC9bB230F00bFFDB560fCe2428b4E05F3442E35',
symbol: 'cUSDC', symbol: 'cUSDC',
decimals: 8 decimals: 8,
}, },
usdt: { usdt: {
instanceAddress: { instanceAddress: {
'100': '0x327853Da7916a6A0935563FB1919A48843036b42', 100: '0x327853Da7916a6A0935563FB1919A48843036b42',
'1000': '0x531AA4DF5858EA1d0031Dad16e3274609DE5AcC0', 1000: '0x531AA4DF5858EA1d0031Dad16e3274609DE5AcC0',
'10000': '0x0958275F0362cf6f07D21373aEE0cf37dFe415dD', 10000: '0x0958275F0362cf6f07D21373aEE0cf37dFe415dD',
'100000': '0x14aEd24B67EaF3FF28503eB92aeb217C47514364' 100000: '0x14aEd24B67EaF3FF28503eB92aeb217C47514364',
}, },
tokenAddress: '0x03c5F29e9296006876d8DF210BCFfD7EA5Db1Cf1', tokenAddress: '0x03c5F29e9296006876d8DF210BCFfD7EA5Db1Cf1',
symbol: 'USDT', symbol: 'USDT',
decimals: 6 decimals: 6,
} },
} },
} },
} }

View File

@ -1,13 +1,13 @@
// https://tornado.cash // https://tornado.cash
/* /*
* d888888P dP a88888b. dP * d888888P dP a88888b. dP
* 88 88 d8' `88 88 * 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b. * 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88 * 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88 * 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP * dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo * ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/ */
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.6.0; pragma solidity ^0.6.0;
@ -23,7 +23,7 @@ contract ERC20Tornado is Tornado {
uint256 _denomination, uint256 _denomination,
uint32 _merkleTreeHeight, uint32 _merkleTreeHeight,
address _token address _token
) Tornado(_verifier, _hasher, _denomination, _merkleTreeHeight) public { ) public Tornado(_verifier, _hasher, _denomination, _merkleTreeHeight) {
token = _token; token = _token;
} }
@ -32,7 +32,12 @@ contract ERC20Tornado is Tornado {
_safeErc20TransferFrom(msg.sender, address(this), denomination); _safeErc20TransferFrom(msg.sender, address(this), denomination);
} }
function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal override { function _processWithdraw(
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) internal override {
require(msg.value == _refund, "Incorrect refund amount received by the contract"); require(msg.value == _refund, "Incorrect refund amount received by the contract");
_safeErc20Transfer(_recipient, denomination - _fee); _safeErc20Transfer(_recipient, denomination - _fee);
@ -49,8 +54,20 @@ contract ERC20Tornado is Tornado {
} }
} }
function _safeErc20TransferFrom(address _from, address _to, uint256 _amount) internal { function _safeErc20TransferFrom(
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd /* transferFrom */, _from, _to, _amount)); address _from,
address _to,
uint256 _amount
) internal {
(bool success, bytes memory data) =
token.call(
abi.encodeWithSelector(
0x23b872dd, /* transferFrom */
_from,
_to,
_amount
)
);
require(success, "not enough allowed tokens"); require(success, "not enough allowed tokens");
// if contract returns some data lets make sure that is `true` according to standard // if contract returns some data lets make sure that is `true` according to standard
@ -62,7 +79,14 @@ contract ERC20Tornado is Tornado {
} }
function _safeErc20Transfer(address _to, uint256 _amount) internal { function _safeErc20Transfer(address _to, uint256 _amount) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb /* transfer */, _to, _amount)); (bool success, bytes memory data) =
token.call(
abi.encodeWithSelector(
0xa9059cbb, /* transfer */
_to,
_amount
)
);
require(success, "not enough tokens"); require(success, "not enough tokens");
// if contract returns some data lets make sure that is `true` according to standard // if contract returns some data lets make sure that is `true` according to standard

View File

@ -1,13 +1,13 @@
// https://tornado.cash // https://tornado.cash
/* /*
* d888888P dP a88888b. dP * d888888P dP a88888b. dP
* 88 88 d8' `88 88 * 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b. * 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88 * 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88 * 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP * dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo * ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/ */
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.6.0; pragma solidity ^0.6.0;
@ -20,14 +20,18 @@ contract ETHTornado is Tornado {
Hasher _hasher, Hasher _hasher,
uint256 _denomination, uint256 _denomination,
uint32 _merkleTreeHeight uint32 _merkleTreeHeight
) Tornado(_verifier, _hasher, _denomination, _merkleTreeHeight) public { ) public Tornado(_verifier, _hasher, _denomination, _merkleTreeHeight) {}
}
function _processDeposit() internal override { function _processDeposit() internal override {
require(msg.value == denomination, "Please send `mixDenomination` ETH along with transaction"); require(msg.value == denomination, "Please send `mixDenomination` ETH along with transaction");
} }
function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal override { function _processWithdraw(
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) internal override {
// sanity checks // sanity checks
require(msg.value == 0, "Message value is supposed to be zero for ETH instance"); 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 instance"); require(_refund == 0, "Refund value is supposed to be zero for ETH instance");

View File

@ -1,13 +1,13 @@
// https://tornado.cash // https://tornado.cash
/* /*
* d888888P dP a88888b. dP * d888888P dP a88888b. dP
* 88 88 d8' `88 88 * 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b. * 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88 * 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88 * 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP * dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo * ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/ */
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.6.0; pragma solidity ^0.6.0;
@ -55,7 +55,11 @@ contract MerkleTreeWithHistory {
/** /**
@dev Hash 2 tree leaves, returns MiMC(_left, _right) @dev Hash 2 tree leaves, returns MiMC(_left, _right)
*/ */
function hashLeftRight(Hasher _hasher, bytes32 _left, bytes32 _right) public pure returns (bytes32) { function hashLeftRight(
Hasher _hasher,
bytes32 _left,
bytes32 _right
) public pure returns (bytes32) {
require(uint256(_left) < FIELD_SIZE, "_left should be inside the field"); require(uint256(_left) < FIELD_SIZE, "_left should be inside the field");
require(uint256(_right) < FIELD_SIZE, "_right should be inside the field"); require(uint256(_right) < FIELD_SIZE, "_right should be inside the field");
uint256 R = uint256(_left); uint256 R = uint256(_left);
@ -66,7 +70,7 @@ contract MerkleTreeWithHistory {
return bytes32(R); return bytes32(R);
} }
function _insert(bytes32 _leaf) internal returns(uint32 index) { function _insert(bytes32 _leaf) internal returns (uint32 index) {
uint32 currentIndex = nextIndex; uint32 currentIndex = nextIndex;
require(currentIndex != uint32(2)**levels, "Merkle tree is full. No more leafs can be added"); require(currentIndex != uint32(2)**levels, "Merkle tree is full. No more leafs can be added");
nextIndex += 1; nextIndex += 1;
@ -98,7 +102,7 @@ contract MerkleTreeWithHistory {
/** /**
@dev Whether the root is present in the root history @dev Whether the root is present in the root history
*/ */
function isKnownRoot(bytes32 _root) public view returns(bool) { function isKnownRoot(bytes32 _root) public view returns (bool) {
if (_root == 0) { if (_root == 0) {
return false; return false;
} }
@ -118,7 +122,7 @@ contract MerkleTreeWithHistory {
/** /**
@dev Returns the last root @dev Returns the last root
*/ */
function getLastRoot() public view returns(bytes32) { function getLastRoot() public view returns (bytes32) {
return roots[currentRootIndex]; return roots[currentRootIndex];
} }
} }

View File

@ -3,7 +3,4 @@ pragma solidity ^0.6.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract ERC20Mock is ERC20 { contract ERC20Mock is ERC20("DAIMock", "DAIM") {}
constructor() ERC20("DAIMock", "DAIM") public {
}
}

View File

@ -3,11 +3,15 @@
pragma solidity ^0.6.0; pragma solidity ^0.6.0;
interface ERC20Basic { interface ERC20Basic {
function _totalSupply() external returns(uint); function _totalSupply() external returns (uint256);
function totalSupply() external view returns (uint);
function balanceOf(address who) external view returns (uint); function totalSupply() external view returns (uint256);
function transfer(address to, uint value) external;
event Transfer(address indexed from, address indexed to, uint value); function balanceOf(address who) external view returns (uint256);
function transfer(address to, uint256 value) external;
event Transfer(address indexed from, address indexed to, uint256 value);
} }
/** /**
@ -15,8 +19,15 @@ interface ERC20Basic {
* @dev see https://github.com/ethereum/EIPs/issues/20 * @dev see https://github.com/ethereum/EIPs/issues/20
*/ */
interface IUSDT is ERC20Basic { interface IUSDT is ERC20Basic {
function allowance(address owner, address spender) external view returns (uint); function allowance(address owner, address spender) external view returns (uint256);
function transferFrom(address from, address to, uint value) external;
function approve(address spender, uint value) external; function transferFrom(
event Approval(address indexed owner, address indexed spender, uint value); address from,
address to,
uint256 value
) external;
function approve(address spender, uint256 value) external;
event Approval(address indexed owner, address indexed spender, uint256 value);
} }

View File

@ -1,11 +1,10 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.6.0; pragma solidity ^0.6.0;
import '../MerkleTreeWithHistory.sol'; import "../MerkleTreeWithHistory.sol";
contract MerkleTreeWithHistoryMock is MerkleTreeWithHistory { contract MerkleTreeWithHistoryMock is MerkleTreeWithHistory {
constructor(uint32 _treeLevels, Hasher _hasher) public MerkleTreeWithHistory(_treeLevels, _hasher) {}
constructor (uint32 _treeLevels, Hasher _hasher) MerkleTreeWithHistory(_treeLevels, _hasher) public {}
function insert(bytes32 _leaf) public { function insert(bytes32 _leaf) public {
_insert(_leaf); _insert(_leaf);

View File

@ -1,13 +1,13 @@
// https://tornado.cash // https://tornado.cash
/* /*
* d888888P dP a88888b. dP * d888888P dP a88888b. dP
* 88 88 d8' `88 88 * 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b. * 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88 * 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88 * 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP * dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo * ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/ */
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.6.0; pragma solidity ^0.6.0;
@ -16,7 +16,7 @@ import "./MerkleTreeWithHistory.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
interface IVerifier { interface IVerifier {
function verifyProof(bytes memory _proof, uint256[6] memory _input) external returns(bool); function verifyProof(bytes memory _proof, uint256[6] memory _input) external returns (bool);
} }
abstract contract Tornado is MerkleTreeWithHistory, ReentrancyGuard { abstract contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
@ -49,7 +49,7 @@ abstract contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
Hasher _hasher, Hasher _hasher,
uint256 _denomination, uint256 _denomination,
uint32 _merkleTreeHeight uint32 _merkleTreeHeight
) MerkleTreeWithHistory(_merkleTreeHeight, _hasher) public { ) public MerkleTreeWithHistory(_merkleTreeHeight, _hasher) {
require(_denomination > 0, "denomination should be greater than 0"); require(_denomination > 0, "denomination should be greater than 0");
verifier = _verifier; verifier = _verifier;
denomination = _denomination; denomination = _denomination;
@ -80,11 +80,25 @@ abstract contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
- 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)
*/ */
function withdraw(bytes calldata _proof, bytes32 _root, bytes32 _nullifierHash, address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) external payable nonReentrant { function withdraw(
bytes calldata _proof,
bytes32 _root,
bytes32 _nullifierHash,
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) external payable nonReentrant {
require(_fee <= denomination, "Fee exceeds transfer value"); require(_fee <= denomination, "Fee exceeds transfer value");
require(!nullifierHashes[_nullifierHash], "The note has been already spent"); 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(isKnownRoot(_root), "Cannot find your merkle root"); // Make sure to use a recent one
require(verifier.verifyProof(_proof, [uint256(_root), uint256(_nullifierHash), uint256(_recipient), uint256(_relayer), _fee, _refund]), "Invalid withdraw proof"); require(
verifier.verifyProof(
_proof,
[uint256(_root), uint256(_nullifierHash), uint256(_recipient), uint256(_relayer), _fee, _refund]
),
"Invalid withdraw proof"
);
nullifierHashes[_nullifierHash] = true; nullifierHashes[_nullifierHash] = true;
_processWithdraw(_recipient, _relayer, _fee, _refund); _processWithdraw(_recipient, _relayer, _fee, _refund);
@ -92,21 +106,25 @@ abstract contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
} }
/** @dev this function is defined in a child contract */ /** @dev this function is defined in a child contract */
function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal virtual; function _processWithdraw(
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) internal virtual;
/** @dev whether a note is already spent */ /** @dev whether a note is already spent */
function isSpent(bytes32 _nullifierHash) public view returns(bool) { function isSpent(bytes32 _nullifierHash) public view returns (bool) {
return nullifierHashes[_nullifierHash]; return nullifierHashes[_nullifierHash];
} }
/** @dev whether an array of notes is already spent */ /** @dev whether an array of notes is already spent */
function isSpentArray(bytes32[] calldata _nullifierHashes) external view returns(bool[] memory spent) { function isSpentArray(bytes32[] calldata _nullifierHashes) external view returns (bool[] memory spent) {
spent = new bool[](_nullifierHashes.length); spent = new bool[](_nullifierHashes.length);
for(uint i = 0; i < _nullifierHashes.length; i++) { for (uint256 i = 0; i < _nullifierHashes.length; i++) {
if (isSpent(_nullifierHashes[i])) { if (isSpent(_nullifierHashes[i])) {
spent[i] = true; spent[i] = true;
} }
} }
} }
} }

View File

@ -11,7 +11,7 @@ async function downloadFile({ url, path }) {
const response = await axios({ const response = await axios({
url, url,
method: 'GET', method: 'GET',
responseType: 'stream' responseType: 'stream',
}) })
response.data.pipe(writer) response.data.pipe(writer)
@ -25,16 +25,16 @@ async function downloadFile({ url, path }) {
async function main() { async function main() {
const release = await axios.get('https://api.github.com/repos/tornadocash/tornado-core/releases/latest') const release = await axios.get('https://api.github.com/repos/tornadocash/tornado-core/releases/latest')
const { assets } = release.data const { assets } = release.data
if (!fs.existsSync(circuitsPath)){ if (!fs.existsSync(circuitsPath)) {
fs.mkdirSync(circuitsPath, { recursive: true }) fs.mkdirSync(circuitsPath, { recursive: true })
fs.mkdirSync(contractsPath, { recursive: true }) fs.mkdirSync(contractsPath, { recursive: true })
} }
for(let asset of assets) { for (let asset of assets) {
if (files.includes(asset.name)) { if (files.includes(asset.name)) {
console.log(`Downloading ${asset.name} ...`) console.log(`Downloading ${asset.name} ...`)
await downloadFile({ await downloadFile({
url: asset.browser_download_url, url: asset.browser_download_url,
path: path.resolve(__dirname, circuitsPath, asset.name) path: path.resolve(__dirname, circuitsPath, asset.name),
}) })
} }
} }

View File

@ -1,16 +1,17 @@
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8" />
<title>Tornado test</title> <title>Tornado test</title>
</head> </head>
<body> <body>
<p> <p>
Open dev console!<br> Open dev console!<br />
Make sure your Metamask is unlocked and connected to Kovan (or other network you've deployed your contract to)<br> Make sure your Metamask is unlocked and connected to Kovan (or other network you've deployed your
contract to)<br />
<a href="#" onclick="deposit()">Deposit</a> <a href="#" onclick="deposit()">Deposit</a>
<a href="#" onclick="withdraw()">Withdraw</a> <a href="#" onclick="withdraw()">Withdraw</a>
</p> </p>
<script src="index.js"></script> <script src="index.js"></script>
</body> </body>
</html> </html>

View File

@ -2,7 +2,6 @@ const jsStorage = require('./Storage')
const hasherImpl = require('./MiMC') const hasherImpl = require('./MiMC')
class MerkleTree { class MerkleTree {
constructor(n_levels, defaultElements, prefix, storage, hasher) { constructor(n_levels, defaultElements, prefix, storage, hasher) {
this.prefix = prefix this.prefix = prefix
this.storage = storage || new jsStorage() this.storage = storage || new jsStorage()
@ -15,9 +14,7 @@ class MerkleTree {
this.zero_values.push(current_zero_value) this.zero_values.push(current_zero_value)
for (let i = 0; i < n_levels; i++) { for (let i = 0; i < n_levels; i++) {
current_zero_value = this.hasher.hash(i, current_zero_value, current_zero_value) current_zero_value = this.hasher.hash(i, current_zero_value, current_zero_value)
this.zero_values.push( this.zero_values.push(current_zero_value.toString())
current_zero_value.toString(),
)
} }
if (defaultElements) { if (defaultElements) {
let level = 0 let level = 0
@ -28,7 +25,7 @@ class MerkleTree {
level++ level++
let numberOfElementsInLevel = Math.ceil(defaultElements.length / 2) let numberOfElementsInLevel = Math.ceil(defaultElements.length / 2)
for (level; level <= this.n_levels; level++) { for (level; level <= this.n_levels; level++) {
for(let i = 0; i < numberOfElementsInLevel; i++) { for (let i = 0; i < numberOfElementsInLevel; i++) {
const leftKey = MerkleTree.index_to_key(prefix, level - 1, 2 * i) const leftKey = MerkleTree.index_to_key(prefix, level - 1, 2 * i)
const rightKey = MerkleTree.index_to_key(prefix, level - 1, 2 * i + 1) const rightKey = MerkleTree.index_to_key(prefix, level - 1, 2 * i + 1)
@ -93,14 +90,14 @@ class MerkleTree {
root, root,
path_elements: traverser.path_elements, path_elements: traverser.path_elements,
path_index: traverser.path_index, path_index: traverser.path_index,
element element,
} }
} }
async update(index, element, insert = false) { async update(index, element, insert = false) {
if (!insert && index >= this.totalElements) { if (!insert && index >= this.totalElements) {
throw Error('Use insert method for new elements.') throw Error('Use insert method for new elements.')
} else if(insert && index < this.totalElements) { } else if (insert && index < this.totalElements) {
throw Error('Use update method for existing elements.') throw Error('Use update method for existing elements.')
} }
try { try {
@ -141,13 +138,7 @@ class MerkleTree {
this.current_element = this.hasher.hash(level, left, right) this.current_element = this.hasher.hash(level, left, right)
} }
} }
let traverser = new UpdateTraverser( let traverser = new UpdateTraverser(this.prefix, this.storage, this.hasher, element, this.zero_values)
this.prefix,
this.storage,
this.hasher,
element,
this.zero_values
)
await this.traverse(index, traverser) await this.traverse(index, traverser)
traverser.key_values_to_put.push({ traverser.key_values_to_put.push({
@ -156,7 +147,7 @@ class MerkleTree {
}) })
await this.storage.put_batch(traverser.key_values_to_put) await this.storage.put_batch(traverser.key_values_to_put)
} catch(e) { } catch (e) {
console.error(e) console.error(e)
} }
} }
@ -182,7 +173,7 @@ class MerkleTree {
} }
getIndexByElement(element) { getIndexByElement(element) {
for(let i = this.totalElements - 1; i >= 0; i--) { for (let i = this.totalElements - 1; i >= 0; i--) {
const elementFromTree = this.storage.get(MerkleTree.index_to_key(this.prefix, 0, i)) const elementFromTree = this.storage.get(MerkleTree.index_to_key(this.prefix, 0, i))
if (elementFromTree === element) { if (elementFromTree === element) {
return i return i

View File

@ -1,5 +1,3 @@
class JsStorage { class JsStorage {
constructor() { constructor() {
this.db = {} this.db = {}
@ -30,7 +28,7 @@ class JsStorage {
} }
put_batch(key_values) { put_batch(key_values) {
key_values.forEach(element => { key_values.forEach((element) => {
this.db[element.key] = element.value this.db[element.key] = element.value
}) })
} }

View File

@ -6,7 +6,7 @@ function send(method, params = []) {
jsonrpc: '2.0', jsonrpc: '2.0',
id: Date.now(), id: Date.now(),
method, method,
params params,
}, (err, res) => { }, (err, res) => {
return err ? reject(err) : resolve(res) return err ? reject(err) : resolve(res)
}) })
@ -48,5 +48,5 @@ module.exports = {
minerStop, minerStop,
minerStart, minerStart,
increaseTime, increaseTime,
traceTransaction traceTransaction,
} }

View File

@ -1,9 +0,0 @@
/* global artifacts */
const Migrations = artifacts.require('Migrations')
module.exports = function(deployer) {
if(deployer.network === 'mainnet') {
return
}
deployer.deploy(Migrations)
}

View File

@ -1,6 +1,6 @@
/* global artifacts */ /* global artifacts */
const Hasher = artifacts.require('Hasher') const Hasher = artifacts.require('Hasher')
module.exports = async function(deployer) { module.exports = async function (deployer) {
await deployer.deploy(Hasher) await deployer.deploy(Hasher)
} }

View File

@ -1,6 +1,6 @@
/* global artifacts */ /* global artifacts */
const Verifier = artifacts.require('Verifier') const Verifier = artifacts.require('Verifier')
module.exports = function(deployer) { module.exports = function (deployer) {
deployer.deploy(Verifier) deployer.deploy(Verifier)
} }

View File

@ -4,14 +4,19 @@ 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')
module.exports = function (deployer, network, accounts) {
module.exports = function(deployer, network, accounts) {
return deployer.then(async () => { return deployer.then(async () => {
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 ETHTornado.link(hasherContract, hasherInstance.address) await ETHTornado.link(hasherContract, hasherInstance.address)
const tornado = await deployer.deploy(ETHTornado, verifier.address, ETH_AMOUNT, MERKLE_TREE_HEIGHT, accounts[0]) const tornado = await deployer.deploy(
console.log('ETHTornado\'s address ', tornado.address) ETHTornado,
verifier.address,
ETH_AMOUNT,
MERKLE_TREE_HEIGHT,
accounts[0],
)
console.log('ETHTornado address ', tornado.address)
}) })
} }

View File

@ -5,15 +5,14 @@ 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')
module.exports = function (deployer, network, accounts) {
module.exports = function(deployer, network, accounts) {
return deployer.then(async () => { return deployer.then(async () => {
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 ERC20Tornado.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
} }
@ -25,6 +24,6 @@ module.exports = function(deployer, network, accounts) {
accounts[0], accounts[0],
token, token,
) )
console.log('ERC20Tornado\'s address ', tornado.address) console.log('ERC20Tornado address ', tornado.address)
}) })
} }

View File

@ -18,13 +18,15 @@ const AMOUNT = '1'
// CURRENCY = 'ETH' // CURRENCY = 'ETH'
/** Generate random number of specified byte length */ /** Generate random number of specified byte length */
const rbigint = nbytes => bigInt.leBuff2int(crypto.randomBytes(nbytes)) const rbigint = (nbytes) => bigInt.leBuff2int(crypto.randomBytes(nbytes))
/** Compute pedersen hash */ /** Compute pedersen hash */
const pedersenHash = data => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0] const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0]
/** BigNumber to hex string of specified length */ /** BigNumber to hex string of specified length */
const toHex = (number, length = 32) => '0x' + (number instanceof Buffer ? number.toString('hex') : bigInt(number).toString(16)).padStart(length * 2, '0') const toHex = (number, length = 32) =>
'0x' +
(number instanceof Buffer ? number.toString('hex') : bigInt(number).toString(16)).padStart(length * 2, '0')
/** /**
* Create deposit object from secret and nullifier * Create deposit object from secret and nullifier
@ -43,7 +45,9 @@ function createDeposit(nullifier, secret) {
async function deposit() { async function deposit() {
const deposit = createDeposit(rbigint(31), rbigint(31)) const deposit = createDeposit(rbigint(31), rbigint(31))
console.log('Sending deposit transaction...') console.log('Sending deposit transaction...')
const tx = await contract.methods.deposit(toHex(deposit.commitment)).send({ value: toWei(AMOUNT), from: web3.eth.defaultAccount, gas:2e6 }) const tx = await contract.methods
.deposit(toHex(deposit.commitment))
.send({ value: toWei(AMOUNT), from: web3.eth.defaultAccount, gas: 2e6 })
console.log(`https://kovan.etherscan.io/tx/${tx.transactionHash}`) console.log(`https://kovan.etherscan.io/tx/${tx.transactionHash}`)
return `tornado-eth-${AMOUNT}-${netId}-${toHex(deposit.preimage, 62)}` return `tornado-eth-${AMOUNT}-${netId}-${toHex(deposit.preimage, 62)}`
} }
@ -87,11 +91,11 @@ async function generateMerkleProof(deposit) {
const events = await contract.getPastEvents('Deposit', { fromBlock: 0, toBlock: 'latest' }) const events = await contract.getPastEvents('Deposit', { fromBlock: 0, 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
.map(e => e.returnValues.commitment) .map((e) => e.returnValues.commitment)
const tree = new merkleTree(MERKLE_TREE_HEIGHT, leaves) const tree = new merkleTree(MERKLE_TREE_HEIGHT, leaves)
// Find current commitment in the tree // Find current commitment in the tree
let depositEvent = events.find(e => e.returnValues.commitment === toHex(deposit.commitment)) let depositEvent = events.find((e) => e.returnValues.commitment === toHex(deposit.commitment))
let leafIndex = depositEvent ? depositEvent.returnValues.leafIndex : -1 let leafIndex = depositEvent ? depositEvent.returnValues.leafIndex : -1
// Validate that our data is correct (optional) // Validate that our data is correct (optional)
@ -141,14 +145,16 @@ async function generateSnarkProof(deposit, recipient) {
toHex(input.recipient, 20), toHex(input.recipient, 20),
toHex(input.relayer, 20), toHex(input.relayer, 20),
toHex(input.fee), toHex(input.fee),
toHex(input.refund) toHex(input.refund),
] ]
return { proof, args } return { proof, args }
} }
async function main() { async function main() {
web3 = new Web3(new Web3.providers.HttpProvider(RPC_URL, { timeout: 5 * 60 * 1000 }), null, { transactionConfirmationBlocks: 1 }) web3 = new Web3(new Web3.providers.HttpProvider(RPC_URL, { timeout: 5 * 60 * 1000 }), null, {
transactionConfirmationBlocks: 1,
})
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
groth16 = await buildGroth16() groth16 = await buildGroth16()

8408
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,10 @@
"migrate:kovan": "npx truffle migrate --network kovan --reset", "migrate:kovan": "npx truffle migrate --network kovan --reset",
"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": "eslint --ext .js --ignore-path .gitignore .",
"prettier:check": "prettier --check . --config .prettierrc",
"prettier:fix": "prettier --write . --config .prettierrc",
"lint": "yarn eslint && yarn prettier:check",
"flat": "npx truffle-flattener contracts/ETHTornado.sol > ETHTornado_flat.sol && npx truffle-flattener contracts/ERC20Tornado.sol > ERC20Tornado_flat.sol", "flat": "npx truffle-flattener contracts/ETHTornado.sol > ETHTornado_flat.sol && npx truffle-flattener contracts/ERC20Tornado.sol > ERC20Tornado_flat.sol",
"download": "node downloadKeys.js" "download": "node downloadKeys.js"
}, },
@ -31,6 +34,7 @@
"@truffle/contract": "^4.0.39", "@truffle/contract": "^4.0.39",
"@truffle/hdwallet-provider": "^1.0.24", "@truffle/hdwallet-provider": "^1.0.24",
"axios": "^0.19.0", "axios": "^0.19.0",
"babel-eslint": "^10.1.0",
"bn-chai": "^1.0.1", "bn-chai": "^1.0.1",
"browserify": "^16.5.0", "browserify": "^16.5.0",
"chai": "^4.2.0", "chai": "^4.2.0",
@ -39,10 +43,15 @@
"circomlib": "git+https://github.com/tornadocash/circomlib.git#c372f14d324d57339c88451834bf2824e73bbdbc", "circomlib": "git+https://github.com/tornadocash/circomlib.git#c372f14d324d57339c88451834bf2824e73bbdbc",
"commander": "^4.1.1", "commander": "^4.1.1",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"eslint": "^6.6.0", "eslint": "^7.19.0",
"eslint-config-prettier": "^7.2.0",
"eslint-plugin-prettier": "^3.3.1",
"eth-json-rpc-filters": "^4.1.1", "eth-json-rpc-filters": "^4.1.1",
"ganache-cli": "^6.7.0", "ganache-cli": "^6.7.0",
"prettier": "^2.2.1",
"prettier-plugin-solidity": "^1.0.0-beta.3",
"snarkjs": "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5", "snarkjs": "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5",
"solhint-plugin-prettier": "^0.0.5",
"truffle": "^5.0.44", "truffle": "^5.0.44",
"truffle-flattener": "^1.4.2", "truffle-flattener": "^1.4.2",
"web3": "^1.2.2", "web3": "^1.2.2",

View File

@ -1,8 +1,5 @@
/* global artifacts, web3, contract */ /* global artifacts, web3, contract */
require('chai') require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should()
.use(require('bn-chai')(web3.utils.BN))
.use(require('chai-as-promised'))
.should()
const fs = require('fs') const fs = require('fs')
const { toBN } = require('web3-utils') const { toBN } = require('web3-utils')
@ -25,7 +22,11 @@ const MerkleTree = require('../lib/MerkleTree')
const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes)) const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes))
const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0] const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0]
const toFixedHex = (number, length = 32) => '0x' + bigInt(number).toString(16).padStart(length * 2, '0') const toFixedHex = (number, length = 32) =>
'0x' +
bigInt(number)
.toString(16)
.padStart(length * 2, '0')
const getRandomRecipient = () => rbigint(20) const getRandomRecipient = () => rbigint(20)
function generateDeposit() { function generateDeposit() {
@ -38,7 +39,7 @@ function generateDeposit() {
return deposit return deposit
} }
contract('ERC20Tornado', accounts => { contract('ERC20Tornado', (accounts) => {
let tornado let tornado
let token let token
let usdtToken let usdtToken
@ -59,11 +60,7 @@ contract('ERC20Tornado', accounts => {
let proving_key let proving_key
before(async () => { before(async () => {
tree = new MerkleTree( tree = new MerkleTree(levels, null, prefix)
levels,
null,
prefix,
)
tornado = await Tornado.deployed() tornado = await Tornado.deployed()
if (ERC20_TOKEN) { if (ERC20_TOKEN) {
token = await Token.at(ERC20_TOKEN) token = await Token.at(ERC20_TOKEN)
@ -142,7 +139,6 @@ contract('ERC20Tornado', accounts => {
pathIndices: path_index, pathIndices: path_index,
}) })
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)
@ -164,7 +160,7 @@ contract('ERC20Tornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
const { logs } = await tornado.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' }) const { logs } = await tornado.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' })
@ -177,7 +173,9 @@ contract('ERC20Tornado', accounts => {
const feeBN = toBN(fee.toString()) const feeBN = toBN(fee.toString())
balanceTornadoAfter.should.be.eq.BN(toBN(balanceTornadoBefore).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)),
)
ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore)) ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore))
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(refund))) ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(refund)))
@ -223,7 +221,6 @@ contract('ERC20Tornado', accounts => {
pathIndices: path_index, pathIndices: path_index,
}) })
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)
@ -243,7 +240,7 @@ contract('ERC20Tornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
const { logs } = await tornado.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' }) const { logs } = await tornado.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' })
@ -256,7 +253,9 @@ contract('ERC20Tornado', accounts => {
const feeBN = toBN(fee.toString()) const feeBN = toBN(fee.toString())
balanceTornadoAfter.should.be.eq.BN(toBN(balanceTornadoBefore).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)),
)
ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore)) ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore))
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore)) ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore))
@ -278,7 +277,6 @@ contract('ERC20Tornado', accounts => {
await token.approve(tornado.address, tokenDenomination, { from: user }) await token.approve(tornado.address, tokenDenomination, { from: user })
await tornado.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)
// Circuit input // Circuit input
const input = stringifyBigInts({ const input = stringifyBigInts({
@ -297,7 +295,6 @@ contract('ERC20Tornado', accounts => {
pathIndices: path_index, pathIndices: path_index,
}) })
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)
@ -307,13 +304,16 @@ contract('ERC20Tornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
let { reason } = await tornado.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 tornado.withdraw(proof, ...args, {
value: toBN(refund).mul(toBN(2)),
;({ reason } = await tornado.withdraw(proof, ...args, { value: toBN(refund).mul(toBN(2)), from: relayer, gasPrice: '0' }).should.be.rejected) 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')
}) })
@ -364,7 +364,6 @@ contract('ERC20Tornado', accounts => {
pathIndices: path_index, pathIndices: path_index,
}) })
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)
@ -385,7 +384,7 @@ contract('ERC20Tornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
const { logs } = await tornado.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' }) const { logs } = await tornado.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' })
@ -401,7 +400,6 @@ contract('ERC20Tornado', accounts => {
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination))) balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination)))
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(refund)).sub(feeBN)) ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(refund)).sub(feeBN))
logs[0].event.should.be.equal('Withdrawal') logs[0].event.should.be.equal('Withdrawal')
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)
@ -453,7 +451,6 @@ contract('ERC20Tornado', accounts => {
pathIndices: path_index, pathIndices: path_index,
}) })
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)
@ -474,7 +471,7 @@ contract('ERC20Tornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
const { logs } = await tornado.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')
@ -491,7 +488,6 @@ contract('ERC20Tornado', accounts => {
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination))) balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination)))
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(refund)).sub(feeBN)) ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(refund)).sub(feeBN))
logs[0].event.should.be.equal('Withdrawal') logs[0].event.should.be.equal('Withdrawal')
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)
@ -505,10 +501,6 @@ contract('ERC20Tornado', accounts => {
await revertSnapshot(snapshotId.result) await revertSnapshot(snapshotId.result)
// eslint-disable-next-line require-atomic-updates // eslint-disable-next-line require-atomic-updates
snapshotId = await takeSnapshot() snapshotId = await takeSnapshot()
tree = new MerkleTree( tree = new MerkleTree(levels, null, prefix)
levels,
null,
prefix,
)
}) })
}) })

View File

@ -1,8 +1,5 @@
/* global artifacts, web3, contract */ /* global artifacts, web3, contract */
require('chai') require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should()
.use(require('bn-chai')(web3.utils.BN))
.use(require('chai-as-promised'))
.should()
const fs = require('fs') const fs = require('fs')
const { toBN, randomHex } = require('web3-utils') const { toBN, randomHex } = require('web3-utils')
@ -23,7 +20,11 @@ const MerkleTree = require('../lib/MerkleTree')
const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes)) const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes))
const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0] const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0]
const toFixedHex = (number, length = 32) => '0x' + bigInt(number).toString(16).padStart(length * 2, '0') const toFixedHex = (number, length = 32) =>
'0x' +
bigInt(number)
.toString(16)
.padStart(length * 2, '0')
const getRandomRecipient = () => rbigint(20) const getRandomRecipient = () => rbigint(20)
function generateDeposit() { function generateDeposit() {
@ -39,7 +40,7 @@ function generateDeposit() {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
function BNArrayToStringArray(array) { function BNArrayToStringArray(array) {
const arrayToPrint = [] const arrayToPrint = []
array.forEach(item => { array.forEach((item) => {
arrayToPrint.push(item.toString()) arrayToPrint.push(item.toString())
}) })
return arrayToPrint return arrayToPrint
@ -51,7 +52,7 @@ function snarkVerify(proof) {
return snarkjs['groth'].isValid(verification_key, proof, proof.publicSignals) return snarkjs['groth'].isValid(verification_key, proof, proof.publicSignals)
} }
contract('ETHTornado', accounts => { contract('ETHTornado', (accounts) => {
let tornado let tornado
const sender = accounts[0] const sender = accounts[0]
const operator = accounts[0] const operator = accounts[0]
@ -69,11 +70,7 @@ contract('ETHTornado', accounts => {
let proving_key let proving_key
before(async () => { before(async () => {
tree = new MerkleTree( tree = new MerkleTree(levels, null, prefix)
levels,
null,
prefix,
)
tornado = await Tornado.deployed() tornado = await Tornado.deployed()
snapshotId = await takeSnapshot() snapshotId = await takeSnapshot()
groth16 = await buildGroth16() groth16 = await buildGroth16()
@ -97,8 +94,8 @@ contract('ETHTornado', accounts => {
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 tornado.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)
@ -138,7 +135,8 @@ contract('ETHTornado', accounts => {
result.should.be.equal(true) result.should.be.equal(true)
// nullifier // nullifier
proofData.publicSignals[1] = '133792158246920651341275668520530514036799294649489851421007411546007850802' proofData.publicSignals[1] =
'133792158246920651341275668520530514036799294649489851421007411546007850802'
result = snarkVerify(proofData) result = snarkVerify(proofData)
result.should.be.equal(false) result.should.be.equal(false)
proofData = originalProof proofData = originalProof
@ -192,7 +190,6 @@ contract('ETHTornado', accounts => {
pathIndices: path_index, pathIndices: path_index,
}) })
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)
@ -212,7 +209,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
const { logs } = await tornado.withdraw(proof, ...args, { from: relayer, gasPrice: '0' }) const { logs } = await tornado.withdraw(proof, ...args, { from: relayer, gasPrice: '0' })
@ -226,7 +223,6 @@ contract('ETHTornado', accounts => {
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))
logs[0].event.should.be.equal('Withdrawal') logs[0].event.should.be.equal('Withdrawal')
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)
@ -262,7 +258,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
await tornado.withdraw(proof, ...args, { from: relayer }).should.be.fulfilled await tornado.withdraw(proof, ...args, { from: relayer }).should.be.fulfilled
const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
@ -292,11 +288,15 @@ contract('ETHTornado', accounts => {
const { proof } = websnarkUtils.toSolidityInput(proofData) const { proof } = websnarkUtils.toSolidityInput(proofData)
const args = [ const args = [
toFixedHex(input.root), toFixedHex(input.root),
toFixedHex(toBN(input.nullifierHash).add(toBN('21888242871839275222246405745257275088548364400416034343698204186575808495617'))), toFixedHex(
toBN(input.nullifierHash).add(
toBN('21888242871839275222246405745257275088548364400416034343698204186575808495617'),
),
),
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
const error = await tornado.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')
@ -330,7 +330,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
const error = await tornado.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')
@ -365,7 +365,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
const error = await tornado.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')
@ -398,7 +398,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
let incorrectArgs let incorrectArgs
const originalProof = proof.slice() const originalProof = proof.slice()
@ -410,7 +410,7 @@ contract('ETHTornado', accounts => {
toFixedHex('0x0000000000000000000000007a1f9131357404ef86d7c38dbffed2da70321337', 20), toFixedHex('0x0000000000000000000000007a1f9131357404ef86d7c38dbffed2da70321337', 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
let error = await tornado.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')
@ -422,7 +422,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex('0x000000000000000000000000000000000000000000000000015345785d8a0000'), toFixedHex('0x000000000000000000000000000000000000000000000000015345785d8a0000'),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
error = await tornado.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')
@ -434,7 +434,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
error = await tornado.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')
@ -476,7 +476,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
const error = await tornado.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 instance') error.reason.should.be.equal('Refund value is supposed to be zero for ETH instance')
@ -502,7 +502,6 @@ contract('ETHTornado', accounts => {
const newOperator = accounts[7] const newOperator = accounts[7]
const error = await tornado.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.')
}) })
}) })
@ -525,7 +524,6 @@ contract('ETHTornado', accounts => {
const newVerifier = accounts[7] const newVerifier = accounts[7]
const error = await tornado.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.')
}) })
}) })
@ -557,7 +555,6 @@ contract('ETHTornado', accounts => {
pathIndices: path_index, pathIndices: path_index,
}) })
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)
@ -567,7 +564,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20), toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20), toFixedHex(input.relayer, 20),
toFixedHex(input.fee), toFixedHex(input.fee),
toFixedHex(input.refund) toFixedHex(input.refund),
] ]
await tornado.withdraw(proof, ...args, { from: relayer, gasPrice: '0' }) await tornado.withdraw(proof, ...args, { from: relayer, gasPrice: '0' })
@ -583,10 +580,6 @@ contract('ETHTornado', accounts => {
await revertSnapshot(snapshotId.result) await revertSnapshot(snapshotId.result)
// eslint-disable-next-line require-atomic-updates // eslint-disable-next-line require-atomic-updates
snapshotId = await takeSnapshot() snapshotId = await takeSnapshot()
tree = new MerkleTree( tree = new MerkleTree(levels, null, prefix)
levels,
null,
prefix,
)
}) })
}) })

View File

@ -1,8 +1,5 @@
/* global artifacts, web3, contract, assert */ /* global artifacts, web3, contract, assert */
require('chai') require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should()
.use(require('bn-chai')(web3.utils.BN))
.use(require('chai-as-promised'))
.should()
const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper') const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper')
@ -20,7 +17,7 @@ const { ETH_AMOUNT, MERKLE_TREE_HEIGHT } = process.env
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
function BNArrayToStringArray(array) { function BNArrayToStringArray(array) {
const arrayToPrint = [] const arrayToPrint = []
array.forEach(item => { array.forEach((item) => {
arrayToPrint.push(item.toString()) arrayToPrint.push(item.toString())
}) })
return arrayToPrint return arrayToPrint
@ -33,7 +30,7 @@ function toFixedHex(number, length = 32) {
return str return str
} }
contract('MerkleTreeWithHistory', accounts => { contract('MerkleTreeWithHistory', (accounts) => {
let merkleTreeWithHistory let merkleTreeWithHistory
let hasherInstance let hasherInstance
let levels = MERKLE_TREE_HEIGHT || 16 let levels = MERKLE_TREE_HEIGHT || 16
@ -46,11 +43,7 @@ contract('MerkleTreeWithHistory', accounts => {
let hasher let hasher
before(async () => { before(async () => {
tree = new MerkleTree( tree = new MerkleTree(levels, null, prefix)
levels,
null,
prefix,
)
hasherInstance = await hasherContract.deployed() hasherInstance = await hasherContract.deployed()
await MerkleTreeWithHistory.link(hasherContract, hasherInstance.address) await MerkleTreeWithHistory.link(hasherContract, hasherInstance.address)
merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels) merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels)
@ -69,40 +62,26 @@ contract('MerkleTreeWithHistory', accounts => {
describe('merkleTreeLib', () => { describe('merkleTreeLib', () => {
it('index_to_key', () => { it('index_to_key', () => {
assert.equal( assert.equal(MerkleTree.index_to_key('test', 5, 20), 'test_tree_5_20')
MerkleTree.index_to_key('test', 5, 20),
'test_tree_5_20',
)
}) })
it('tests insert', async () => { it('tests insert', async () => {
hasher = new hasherImpl() hasher = new hasherImpl()
tree = new MerkleTree( tree = new MerkleTree(2, null, prefix)
2,
null,
prefix,
)
await tree.insert(toFixedHex('5')) await tree.insert(toFixedHex('5'))
let { root, path_elements } = await tree.path(0) let { root, path_elements } = await tree.path(0)
const calculated_root = hasher.hash(null, const calculated_root = hasher.hash(null, hasher.hash(null, '5', path_elements[0]), path_elements[1])
hasher.hash(null, '5', path_elements[0]),
path_elements[1]
)
// console.log(root) // console.log(root)
assert.equal(root, calculated_root) assert.equal(root, calculated_root)
}) })
it('creation odd elements count', async () => { it('creation odd elements count', async () => {
const elements = [12, 13, 14, 15, 16, 17, 18, 19, 20] const elements = [12, 13, 14, 15, 16, 17, 18, 19, 20]
for(const [, el] of Object.entries(elements)) { for (const [, el] of Object.entries(elements)) {
await tree.insert(el) await tree.insert(el)
} }
const batchTree = new MerkleTree( const batchTree = new MerkleTree(levels, elements, prefix)
levels, for (const [i] of Object.entries(elements)) {
elements,
prefix,
)
for(const [i] of Object.entries(elements)) {
const pathViaConstructor = await batchTree.path(i) const pathViaConstructor = await batchTree.path(i)
const pathViaUpdate = await tree.path(i) const pathViaUpdate = await tree.path(i)
pathViaConstructor.should.be.deep.equal(pathViaUpdate) pathViaConstructor.should.be.deep.equal(pathViaUpdate)
@ -111,7 +90,7 @@ contract('MerkleTreeWithHistory', accounts => {
it('should find an element', async () => { it('should find an element', async () => {
const elements = [12, 13, 14, 15, 16, 17, 18, 19, 20] const elements = [12, 13, 14, 15, 16, 17, 18, 19, 20]
for(const [, el] of Object.entries(elements)) { for (const [, el] of Object.entries(elements)) {
await tree.insert(el) await tree.insert(el)
} }
let index = tree.getIndexByElement(13) let index = tree.getIndexByElement(13)
@ -132,16 +111,12 @@ contract('MerkleTreeWithHistory', accounts => {
it('creation even elements count', async () => { it('creation even elements count', async () => {
const elements = [12, 13, 14, 15, 16, 17] const elements = [12, 13, 14, 15, 16, 17]
for(const [, el] of Object.entries(elements)) { for (const [, el] of Object.entries(elements)) {
await tree.insert(el) await tree.insert(el)
} }
const batchTree = new MerkleTree( const batchTree = new MerkleTree(levels, elements, prefix)
levels, for (const [i] of Object.entries(elements)) {
elements,
prefix,
)
for(const [i] of Object.entries(elements)) {
const pathViaConstructor = await batchTree.path(i) const pathViaConstructor = await batchTree.path(i)
const pathViaUpdate = await tree.path(i) const pathViaUpdate = await tree.path(i)
pathViaConstructor.should.be.deep.equal(pathViaUpdate) pathViaConstructor.should.be.deep.equal(pathViaUpdate)
@ -150,15 +125,11 @@ contract('MerkleTreeWithHistory', accounts => {
it.skip('creation using 30000 elements', () => { it.skip('creation using 30000 elements', () => {
const elements = [] const elements = []
for(let i = 1000; i < 31001; i++) { for (let i = 1000; i < 31001; i++) {
elements.push(i) elements.push(i)
} }
console.time('MerkleTree') console.time('MerkleTree')
tree = new MerkleTree( tree = new MerkleTree(levels, elements, prefix)
levels,
elements,
prefix,
)
console.timeEnd('MerkleTree') console.timeEnd('MerkleTree')
// 2,7 GHz Intel Core i7 // 2,7 GHz Intel Core i7
// 1000 : 1949.084ms // 1000 : 1949.084ms
@ -184,8 +155,8 @@ contract('MerkleTreeWithHistory', accounts => {
const levels = 6 const levels = 6
const merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels) const merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels)
for (let i = 0; i < 2**levels; i++) { for (let i = 0; i < 2 ** levels; i++) {
await merkleTreeWithHistory.insert(toFixedHex(i+42)).should.be.fulfilled await merkleTreeWithHistory.insert(toFixedHex(i + 42)).should.be.fulfilled
} }
let error = await merkleTreeWithHistory.insert(toFixedHex(1337)).should.be.rejected let error = await merkleTreeWithHistory.insert(toFixedHex(1337)).should.be.rejected
@ -230,18 +201,11 @@ contract('MerkleTreeWithHistory', accounts => {
}) })
}) })
afterEach(async () => { afterEach(async () => {
await revertSnapshot(snapshotId.result) await revertSnapshot(snapshotId.result)
// eslint-disable-next-line require-atomic-updates // eslint-disable-next-line require-atomic-updates
snapshotId = await takeSnapshot() snapshotId = await takeSnapshot()
hasher = new hasherImpl() hasher = new hasherImpl()
tree = new MerkleTree( tree = new MerkleTree(levels, null, prefix, null, hasher)
levels,
null,
prefix,
null,
hasher,
)
}) })
}) })

View File

@ -1,6 +1,6 @@
require("dotenv").config(); require('dotenv').config()
const HDWalletProvider = require("@truffle/hdwallet-provider"); const HDWalletProvider = require('@truffle/hdwallet-provider')
const utils = require("web3-utils"); const utils = require('web3-utils')
// const infuraKey = "fj4jll3k....."; // const infuraKey = "fj4jll3k.....";
// //
// const fs = require('fs'); // const fs = require('fs');
@ -25,9 +25,9 @@ module.exports = {
// options below to some value. // options below to some value.
development: { development: {
host: "127.0.0.1", // Localhost (default: none) host: '127.0.0.1', // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none) port: 8545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none) network_id: '*', // Any network (default: none)
}, },
// Another network with more advanced options... // Another network with more advanced options...
@ -46,11 +46,11 @@ module.exports = {
provider: () => provider: () =>
new HDWalletProvider( new HDWalletProvider(
process.env.PRIVATE_KEY, process.env.PRIVATE_KEY,
"https://kovan.infura.io/v3/97c8bf358b9942a9853fab1ba93dc5b3" 'https://kovan.infura.io/v3/97c8bf358b9942a9853fab1ba93dc5b3',
), ),
network_id: 42, network_id: 42,
gas: 6000000, gas: 6000000,
gasPrice: utils.toWei("1", "gwei"), gasPrice: utils.toWei('1', 'gwei'),
// confirmations: 0, // confirmations: 0,
// timeoutBlocks: 200, // timeoutBlocks: 200,
skipDryRun: true, skipDryRun: true,
@ -59,11 +59,11 @@ module.exports = {
provider: () => provider: () =>
new HDWalletProvider( new HDWalletProvider(
process.env.PRIVATE_KEY, process.env.PRIVATE_KEY,
"https://goerli.infura.io/v3/d34c08f2cb7c4111b645d06ac7e35ba8" 'https://goerli.infura.io/v3/d34c08f2cb7c4111b645d06ac7e35ba8',
), ),
network_id: 5, network_id: 5,
gas: 6000000, gas: 6000000,
gasPrice: utils.toWei("1", "gwei"), gasPrice: utils.toWei('1', 'gwei'),
// confirmations: 0, // confirmations: 0,
// timeoutBlocks: 200, // timeoutBlocks: 200,
skipDryRun: true, skipDryRun: true,
@ -72,24 +72,20 @@ module.exports = {
provider: () => provider: () =>
new HDWalletProvider( new HDWalletProvider(
process.env.PRIVATE_KEY, process.env.PRIVATE_KEY,
"https://rinkeby.infura.io/v3/97c8bf358b9942a9853fab1ba93dc5b3" 'https://rinkeby.infura.io/v3/97c8bf358b9942a9853fab1ba93dc5b3',
), ),
network_id: 4, network_id: 4,
gas: 6000000, gas: 6000000,
gasPrice: utils.toWei("1", "gwei"), gasPrice: utils.toWei('1', 'gwei'),
// confirmations: 0, // confirmations: 0,
// timeoutBlocks: 200, // timeoutBlocks: 200,
skipDryRun: true, skipDryRun: true,
}, },
mainnet: { mainnet: {
provider: () => provider: () => new HDWalletProvider(process.env.PRIVATE_KEY, 'http://ethereum-rpc.trustwalletapp.com'),
new HDWalletProvider(
process.env.PRIVATE_KEY,
"http://ethereum-rpc.trustwalletapp.com"
),
network_id: 1, network_id: 1,
gas: 6000000, gas: 6000000,
gasPrice: utils.toWei("2", "gwei"), gasPrice: utils.toWei('2', 'gwei'),
// confirmations: 0, // confirmations: 0,
// timeoutBlocks: 200, // timeoutBlocks: 200,
skipDryRun: true, skipDryRun: true,
@ -111,7 +107,7 @@ module.exports = {
// Configure your compilers // Configure your compilers
compilers: { compilers: {
solc: { solc: {
version: "0.6.12", // Fetch exact version from solc-bin (default: truffle's version) version: '0.6.12', // Fetch exact version from solc-bin (default: truffle's version)
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false) // docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
settings: { settings: {
// See the solidity docs for advice about optimization and evmVersion // See the solidity docs for advice about optimization and evmVersion
@ -123,12 +119,12 @@ module.exports = {
}, },
}, },
external: { external: {
command: "node ./compileHasher.js", command: 'node ./compileHasher.js',
targets: [ targets: [
{ {
path: "./build/Hasher.json", path: './build/Hasher.json',
}, },
], ],
}, },
}, },
}; }

558
yarn.lock

File diff suppressed because it is too large Load Diff