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",
"rules": {
"indent": ["error", 2]
}
"prettier/prettier": [
"error",
{
"printWidth": 110
}
],
"quotes": ["error", "double"]
},
"plugins": ["prettier"]
}

View File

@ -1,7 +1,7 @@
dist: trusty
language: node_js
node_js:
- "11"
- '11'
install:
- npm ci
- 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.relayer, 20),
toHex(input.fee),
toHex(input.refund)
toHex(input.refund),
]
return { proof, args }
@ -265,7 +265,7 @@ function fromDecimals({ amount, decimals }) {
const comps = ether.split('.')
if (comps.length > 2) {
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) {
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 {
const eventWhenHappened = await tornado.getPastEvents('Deposit', {
filter: {
commitment: deposit.commitmentHex
commitment: deposit.commitmentHex,
},
fromBlock: 0,
toBlock: 'latest'
toBlock: 'latest',
})
if (eventWhenHappened.length === 0) {
throw new Error('There is no related deposit, the note is invalid')
@ -441,7 +441,7 @@ async function loadWithdrawalData({ amount, currency, deposit }) {
try {
const events = await await tornado.getPastEvents('Withdrawal', {
fromBlock: 0,
toBlock: 'latest'
toBlock: 'latest',
})
const withdrawEvent = events.filter((event) => {
@ -451,7 +451,7 @@ async function loadWithdrawalData({ amount, currency, deposit }) {
const fee = withdrawEvent.returnValues.fee
const decimals = config.deployments[`netId${netId}`][currency].decimals
const withdrawalAmount = toBN(fromDecimals({ amount, decimals })).sub(
toBN(fee)
toBN(fee),
)
const { timestamp } = await web3.eth.getBlock(withdrawEvent.blockHash)
return {
@ -460,7 +460,7 @@ async function loadWithdrawalData({ amount, currency, deposit }) {
to: withdrawEvent.returnValues.to,
timestamp,
nullifier: deposit.nullifierHex,
fee: toDecimals(fee, decimals, 9)
fee: toDecimals(fee, decimals, 9),
}
} catch (e) {
console.error('loadWithdrawalData', e)

View File

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

128
config.js
View File

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

View File

@ -1,13 +1,13 @@
// https://tornado.cash
/*
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
@ -23,7 +23,7 @@ contract ERC20Tornado is Tornado {
uint256 _denomination,
uint32 _merkleTreeHeight,
address _token
) Tornado(_verifier, _hasher, _denomination, _merkleTreeHeight) public {
) public Tornado(_verifier, _hasher, _denomination, _merkleTreeHeight) {
token = _token;
}
@ -32,7 +32,12 @@ contract ERC20Tornado is Tornado {
_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");
_safeErc20Transfer(_recipient, denomination - _fee);
@ -49,8 +54,20 @@ contract ERC20Tornado is Tornado {
}
}
function _safeErc20TransferFrom(address _from, address _to, uint256 _amount) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd /* transferFrom */, _from, _to, _amount));
function _safeErc20TransferFrom(
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");
// 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 {
(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");
// if contract returns some data lets make sure that is `true` according to standard

View File

@ -1,13 +1,13 @@
// https://tornado.cash
/*
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
@ -20,14 +20,18 @@ contract ETHTornado is Tornado {
Hasher _hasher,
uint256 _denomination,
uint32 _merkleTreeHeight
) Tornado(_verifier, _hasher, _denomination, _merkleTreeHeight) public {
}
) public Tornado(_verifier, _hasher, _denomination, _merkleTreeHeight) {}
function _processDeposit() internal override {
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
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");

View File

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

View File

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

View File

@ -3,11 +3,15 @@
pragma solidity ^0.6.0;
interface ERC20Basic {
function _totalSupply() external returns(uint);
function totalSupply() external view returns (uint);
function balanceOf(address who) external view returns (uint);
function transfer(address to, uint value) external;
event Transfer(address indexed from, address indexed to, uint value);
function _totalSupply() external returns (uint256);
function totalSupply() external view returns (uint256);
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
*/
interface IUSDT is ERC20Basic {
function allowance(address owner, address spender) external view returns (uint);
function transferFrom(address from, address to, uint value) external;
function approve(address spender, uint value) external;
event Approval(address indexed owner, address indexed spender, uint value);
function allowance(address owner, address spender) external view returns (uint256);
function transferFrom(
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,13 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import '../MerkleTreeWithHistory.sol';
import "../MerkleTreeWithHistory.sol";
contract MerkleTreeWithHistoryMock is MerkleTreeWithHistory {
constructor (uint32 _treeLevels, Hasher _hasher) MerkleTreeWithHistory(_treeLevels, _hasher) public {}
constructor(uint32 _treeLevels, Hasher _hasher) public MerkleTreeWithHistory(_treeLevels, _hasher) {}
function insert(bytes32 _leaf) public {
_insert(_leaf);
_insert(_leaf);
}
}

View File

@ -1,13 +1,13 @@
// https://tornado.cash
/*
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
@ -16,7 +16,7 @@ import "./MerkleTreeWithHistory.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
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 {
@ -49,7 +49,7 @@ abstract contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
Hasher _hasher,
uint256 _denomination,
uint32 _merkleTreeHeight
) MerkleTreeWithHistory(_merkleTreeHeight, _hasher) public {
) public MerkleTreeWithHistory(_merkleTreeHeight, _hasher) {
require(_denomination > 0, "denomination should be greater than 0");
verifier = _verifier;
denomination = _denomination;
@ -80,11 +80,25 @@ abstract contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
- the recipient of funds
- 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(!nullifierHashes[_nullifierHash], "The note has been already spent");
require(isKnownRoot(_root), "Cannot find your merkle root"); // Make sure to use a recent one
require(verifier.verifyProof(_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;
_processWithdraw(_recipient, _relayer, _fee, _refund);
@ -92,21 +106,25 @@ abstract contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
}
/** @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 */
function isSpent(bytes32 _nullifierHash) public view returns(bool) {
function isSpent(bytes32 _nullifierHash) public view returns (bool) {
return nullifierHashes[_nullifierHash];
}
/** @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);
for(uint i = 0; i < _nullifierHashes.length; i++) {
for (uint256 i = 0; i < _nullifierHashes.length; i++) {
if (isSpent(_nullifierHashes[i])) {
spent[i] = true;
}
}
}
}

View File

@ -11,7 +11,7 @@ async function downloadFile({ url, path }) {
const response = await axios({
url,
method: 'GET',
responseType: 'stream'
responseType: 'stream',
})
response.data.pipe(writer)
@ -25,16 +25,16 @@ async function downloadFile({ url, path }) {
async function main() {
const release = await axios.get('https://api.github.com/repos/tornadocash/tornado-core/releases/latest')
const { assets } = release.data
if (!fs.existsSync(circuitsPath)){
if (!fs.existsSync(circuitsPath)) {
fs.mkdirSync(circuitsPath, { recursive: true })
fs.mkdirSync(contractsPath, { recursive: true })
}
for(let asset of assets) {
for (let asset of assets) {
if (files.includes(asset.name)) {
console.log(`Downloading ${asset.name} ...`)
await downloadFile({
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">
<head>
<meta charset="utf-8">
<title>Tornado test</title>
</head>
<body>
<p>
Open dev console!<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="withdraw()">Withdraw</a>
</p>
<script src="index.js"></script>
</body>
<head>
<meta charset="utf-8" />
<title>Tornado test</title>
</head>
<body>
<p>
Open dev console!<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="withdraw()">Withdraw</a>
</p>
<script src="index.js"></script>
</body>
</html>

View File

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

View File

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

View File

@ -6,7 +6,7 @@ function send(method, params = []) {
jsonrpc: '2.0',
id: Date.now(),
method,
params
params,
}, (err, res) => {
return err ? reject(err) : resolve(res)
})
@ -48,5 +48,5 @@ module.exports = {
minerStop,
minerStart,
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 */
const Hasher = artifacts.require('Hasher')
module.exports = async function(deployer) {
module.exports = async function (deployer) {
await deployer.deploy(Hasher)
}

View File

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

View File

@ -4,14 +4,19 @@ const ETHTornado = artifacts.require('ETHTornado')
const Verifier = artifacts.require('Verifier')
const hasherContract = artifacts.require('Hasher')
module.exports = function(deployer, network, accounts) {
module.exports = function (deployer, network, accounts) {
return deployer.then(async () => {
const { MERKLE_TREE_HEIGHT, ETH_AMOUNT } = process.env
const verifier = await Verifier.deployed()
const hasherInstance = await hasherContract.deployed()
await ETHTornado.link(hasherContract, hasherInstance.address)
const tornado = await deployer.deploy(ETHTornado, verifier.address, ETH_AMOUNT, MERKLE_TREE_HEIGHT, accounts[0])
console.log('ETHTornado\'s address ', tornado.address)
const tornado = await deployer.deploy(
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 ERC20Mock = artifacts.require('ERC20Mock')
module.exports = function(deployer, network, accounts) {
module.exports = function (deployer, network, accounts) {
return deployer.then(async () => {
const { MERKLE_TREE_HEIGHT, ERC20_TOKEN, TOKEN_AMOUNT } = process.env
const verifier = await Verifier.deployed()
const hasherInstance = await hasherContract.deployed()
await ERC20Tornado.link(hasherContract, hasherInstance.address)
let token = ERC20_TOKEN
if(token === '') {
if (token === '') {
const tokenInstance = await deployer.deploy(ERC20Mock)
token = tokenInstance.address
}
@ -25,6 +24,6 @@ module.exports = function(deployer, network, accounts) {
accounts[0],
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'
/** 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 */
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 */
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
@ -43,7 +45,9 @@ function createDeposit(nullifier, secret) {
async function deposit() {
const deposit = createDeposit(rbigint(31), rbigint(31))
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}`)
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 leaves = events
.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)
// 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
// Validate that our data is correct (optional)
@ -141,14 +145,16 @@ async function generateSnarkProof(deposit, recipient) {
toHex(input.recipient, 20),
toHex(input.relayer, 20),
toHex(input.fee),
toHex(input.refund)
toHex(input.refund),
]
return { proof, args }
}
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')
proving_key = fs.readFileSync('build/circuits/withdraw_proving_key.bin').buffer
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:rinkeby": "npx truffle migrate --network rinkeby --reset",
"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",
"download": "node downloadKeys.js"
},
@ -31,6 +34,7 @@
"@truffle/contract": "^4.0.39",
"@truffle/hdwallet-provider": "^1.0.24",
"axios": "^0.19.0",
"babel-eslint": "^10.1.0",
"bn-chai": "^1.0.1",
"browserify": "^16.5.0",
"chai": "^4.2.0",
@ -39,10 +43,15 @@
"circomlib": "git+https://github.com/tornadocash/circomlib.git#c372f14d324d57339c88451834bf2824e73bbdbc",
"commander": "^4.1.1",
"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",
"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",
"solhint-plugin-prettier": "^0.0.5",
"truffle": "^5.0.44",
"truffle-flattener": "^1.4.2",
"web3": "^1.2.2",

View File

@ -1,8 +1,5 @@
/* global artifacts, web3, contract */
require('chai')
.use(require('bn-chai')(web3.utils.BN))
.use(require('chai-as-promised'))
.should()
require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should()
const fs = require('fs')
const { toBN } = require('web3-utils')
@ -25,7 +22,11 @@ const MerkleTree = require('../lib/MerkleTree')
const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes))
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)
function generateDeposit() {
@ -38,7 +39,7 @@ function generateDeposit() {
return deposit
}
contract('ERC20Tornado', accounts => {
contract('ERC20Tornado', (accounts) => {
let tornado
let token
let usdtToken
@ -59,11 +60,7 @@ contract('ERC20Tornado', accounts => {
let proving_key
before(async () => {
tree = new MerkleTree(
levels,
null,
prefix,
)
tree = new MerkleTree(levels, null, prefix)
tornado = await Tornado.deployed()
if (ERC20_TOKEN) {
token = await Token.at(ERC20_TOKEN)
@ -142,7 +139,6 @@ contract('ERC20Tornado', accounts => {
pathIndices: path_index,
})
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
const { proof } = websnarkUtils.toSolidityInput(proofData)
@ -164,7 +160,7 @@ contract('ERC20Tornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
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())
balanceTornadoAfter.should.be.eq.BN(toBN(balanceTornadoBefore).sub(toBN(tokenDenomination)))
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))
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(refund)))
@ -223,7 +221,6 @@ contract('ERC20Tornado', accounts => {
pathIndices: path_index,
})
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
const { proof } = websnarkUtils.toSolidityInput(proofData)
@ -243,7 +240,7 @@ contract('ERC20Tornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
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())
balanceTornadoAfter.should.be.eq.BN(toBN(balanceTornadoBefore).sub(toBN(tokenDenomination)))
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))
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore))
@ -278,7 +277,6 @@ contract('ERC20Tornado', accounts => {
await token.approve(tornado.address, tokenDenomination, { from: user })
await tornado.deposit(toFixedHex(deposit.commitment), { from: user, gasPrice: '0' })
const { root, path_elements, path_index } = await tree.path(0)
// Circuit input
const input = stringifyBigInts({
@ -297,7 +295,6 @@ contract('ERC20Tornado', accounts => {
pathIndices: path_index,
})
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
const { proof } = websnarkUtils.toSolidityInput(proofData)
@ -307,13 +304,16 @@ contract('ERC20Tornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
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 } = await tornado.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')
})
@ -364,7 +364,6 @@ contract('ERC20Tornado', accounts => {
pathIndices: path_index,
})
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
const { proof } = websnarkUtils.toSolidityInput(proofData)
@ -385,7 +384,7 @@ contract('ERC20Tornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
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)))
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(refund)).sub(feeBN))
logs[0].event.should.be.equal('Withdrawal')
logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
logs[0].args.relayer.should.be.eq.BN(operator)
@ -453,7 +451,6 @@ contract('ERC20Tornado', accounts => {
pathIndices: path_index,
})
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
const { proof } = websnarkUtils.toSolidityInput(proofData)
@ -474,7 +471,7 @@ contract('ERC20Tornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
const { logs } = await tornado.withdraw(proof, ...args, { value: refund, from: relayer, gasPrice: '0' })
console.log('withdraw done')
@ -491,7 +488,6 @@ contract('ERC20Tornado', accounts => {
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination)))
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(refund)).sub(feeBN))
logs[0].event.should.be.equal('Withdrawal')
logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
logs[0].args.relayer.should.be.eq.BN(operator)
@ -505,10 +501,6 @@ contract('ERC20Tornado', accounts => {
await revertSnapshot(snapshotId.result)
// eslint-disable-next-line require-atomic-updates
snapshotId = await takeSnapshot()
tree = new MerkleTree(
levels,
null,
prefix,
)
tree = new MerkleTree(levels, null, prefix)
})
})

View File

@ -1,8 +1,5 @@
/* global artifacts, web3, contract */
require('chai')
.use(require('bn-chai')(web3.utils.BN))
.use(require('chai-as-promised'))
.should()
require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should()
const fs = require('fs')
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 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)
function generateDeposit() {
@ -39,7 +40,7 @@ function generateDeposit() {
// eslint-disable-next-line no-unused-vars
function BNArrayToStringArray(array) {
const arrayToPrint = []
array.forEach(item => {
array.forEach((item) => {
arrayToPrint.push(item.toString())
})
return arrayToPrint
@ -51,7 +52,7 @@ function snarkVerify(proof) {
return snarkjs['groth'].isValid(verification_key, proof, proof.publicSignals)
}
contract('ETHTornado', accounts => {
contract('ETHTornado', (accounts) => {
let tornado
const sender = accounts[0]
const operator = accounts[0]
@ -69,11 +70,7 @@ contract('ETHTornado', accounts => {
let proving_key
before(async () => {
tree = new MerkleTree(
levels,
null,
prefix,
)
tree = new MerkleTree(levels, null, prefix)
tornado = await Tornado.deployed()
snapshotId = await takeSnapshot()
groth16 = await buildGroth16()
@ -97,8 +94,8 @@ contract('ETHTornado', accounts => {
logs[0].args.commitment.should.be.equal(commitment)
logs[0].args.leafIndex.should.be.eq.BN(0)
commitment = toFixedHex(12);
({ logs } = await tornado.deposit(commitment, { value, from: accounts[2] }))
commitment = toFixedHex(12)
;({ logs } = await tornado.deposit(commitment, { value, from: accounts[2] }))
logs[0].event.should.be.equal('Deposit')
logs[0].args.commitment.should.be.equal(commitment)
@ -138,7 +135,8 @@ contract('ETHTornado', accounts => {
result.should.be.equal(true)
// nullifier
proofData.publicSignals[1] = '133792158246920651341275668520530514036799294649489851421007411546007850802'
proofData.publicSignals[1] =
'133792158246920651341275668520530514036799294649489851421007411546007850802'
result = snarkVerify(proofData)
result.should.be.equal(false)
proofData = originalProof
@ -192,7 +190,6 @@ contract('ETHTornado', accounts => {
pathIndices: path_index,
})
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
const { proof } = websnarkUtils.toSolidityInput(proofData)
@ -212,7 +209,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
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))
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(value)).sub(feeBN))
logs[0].event.should.be.equal('Withdrawal')
logs[0].args.nullifierHash.should.be.equal(toFixedHex(input.nullifierHash))
logs[0].args.relayer.should.be.eq.BN(operator)
@ -262,7 +258,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
await tornado.withdraw(proof, ...args, { from: relayer }).should.be.fulfilled
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 args = [
toFixedHex(input.root),
toFixedHex(toBN(input.nullifierHash).add(toBN('21888242871839275222246405745257275088548364400416034343698204186575808495617'))),
toFixedHex(
toBN(input.nullifierHash).add(
toBN('21888242871839275222246405745257275088548364400416034343698204186575808495617'),
),
),
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
error.reason.should.be.equal('verifier-gte-snark-scalar-field')
@ -330,7 +330,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
error.reason.should.be.equal('Fee exceeds transfer value')
@ -365,7 +365,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
error.reason.should.be.equal('Cannot find your merkle root')
@ -398,7 +398,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
let incorrectArgs
const originalProof = proof.slice()
@ -410,7 +410,7 @@ contract('ETHTornado', accounts => {
toFixedHex('0x0000000000000000000000007a1f9131357404ef86d7c38dbffed2da70321337', 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
let error = await tornado.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected
error.reason.should.be.equal('Invalid withdraw proof')
@ -422,7 +422,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex('0x000000000000000000000000000000000000000000000000015345785d8a0000'),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
error = await tornado.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected
error.reason.should.be.equal('Invalid withdraw proof')
@ -434,7 +434,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
error = await tornado.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected
error.reason.should.be.equal('Invalid withdraw proof')
@ -476,7 +476,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
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')
@ -500,9 +500,8 @@ contract('ETHTornado', accounts => {
operator.should.be.equal(sender)
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.')
})
})
@ -523,9 +522,8 @@ contract('ETHTornado', accounts => {
operator.should.be.equal(sender)
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.')
})
})
@ -557,7 +555,6 @@ contract('ETHTornado', accounts => {
pathIndices: path_index,
})
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
const { proof } = websnarkUtils.toSolidityInput(proofData)
@ -567,7 +564,7 @@ contract('ETHTornado', accounts => {
toFixedHex(input.recipient, 20),
toFixedHex(input.relayer, 20),
toFixedHex(input.fee),
toFixedHex(input.refund)
toFixedHex(input.refund),
]
await tornado.withdraw(proof, ...args, { from: relayer, gasPrice: '0' })
@ -583,10 +580,6 @@ contract('ETHTornado', accounts => {
await revertSnapshot(snapshotId.result)
// eslint-disable-next-line require-atomic-updates
snapshotId = await takeSnapshot()
tree = new MerkleTree(
levels,
null,
prefix,
)
tree = new MerkleTree(levels, null, prefix)
})
})

View File

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

View File

@ -1,6 +1,6 @@
require("dotenv").config();
const HDWalletProvider = require("@truffle/hdwallet-provider");
const utils = require("web3-utils");
require('dotenv').config()
const HDWalletProvider = require('@truffle/hdwallet-provider')
const utils = require('web3-utils')
// const infuraKey = "fj4jll3k.....";
//
// const fs = require('fs');
@ -25,9 +25,9 @@ module.exports = {
// options below to some value.
development: {
host: "127.0.0.1", // Localhost (default: none)
host: '127.0.0.1', // Localhost (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...
@ -46,11 +46,11 @@ module.exports = {
provider: () =>
new HDWalletProvider(
process.env.PRIVATE_KEY,
"https://kovan.infura.io/v3/97c8bf358b9942a9853fab1ba93dc5b3"
'https://kovan.infura.io/v3/97c8bf358b9942a9853fab1ba93dc5b3',
),
network_id: 42,
gas: 6000000,
gasPrice: utils.toWei("1", "gwei"),
gasPrice: utils.toWei('1', 'gwei'),
// confirmations: 0,
// timeoutBlocks: 200,
skipDryRun: true,
@ -59,11 +59,11 @@ module.exports = {
provider: () =>
new HDWalletProvider(
process.env.PRIVATE_KEY,
"https://goerli.infura.io/v3/d34c08f2cb7c4111b645d06ac7e35ba8"
'https://goerli.infura.io/v3/d34c08f2cb7c4111b645d06ac7e35ba8',
),
network_id: 5,
gas: 6000000,
gasPrice: utils.toWei("1", "gwei"),
gasPrice: utils.toWei('1', 'gwei'),
// confirmations: 0,
// timeoutBlocks: 200,
skipDryRun: true,
@ -72,24 +72,20 @@ module.exports = {
provider: () =>
new HDWalletProvider(
process.env.PRIVATE_KEY,
"https://rinkeby.infura.io/v3/97c8bf358b9942a9853fab1ba93dc5b3"
'https://rinkeby.infura.io/v3/97c8bf358b9942a9853fab1ba93dc5b3',
),
network_id: 4,
gas: 6000000,
gasPrice: utils.toWei("1", "gwei"),
gasPrice: utils.toWei('1', 'gwei'),
// confirmations: 0,
// timeoutBlocks: 200,
skipDryRun: true,
},
mainnet: {
provider: () =>
new HDWalletProvider(
process.env.PRIVATE_KEY,
"http://ethereum-rpc.trustwalletapp.com"
),
provider: () => new HDWalletProvider(process.env.PRIVATE_KEY, 'http://ethereum-rpc.trustwalletapp.com'),
network_id: 1,
gas: 6000000,
gasPrice: utils.toWei("2", "gwei"),
gasPrice: utils.toWei('2', 'gwei'),
// confirmations: 0,
// timeoutBlocks: 200,
skipDryRun: true,
@ -111,7 +107,7 @@ module.exports = {
// Configure your compilers
compilers: {
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)
settings: {
// See the solidity docs for advice about optimization and evmVersion
@ -123,12 +119,12 @@ module.exports = {
},
},
external: {
command: "node ./compileHasher.js",
command: 'node ./compileHasher.js',
targets: [
{
path: "./build/Hasher.json",
path: './build/Hasher.json',
},
],
},
},
};
}

558
yarn.lock

File diff suppressed because it is too large Load Diff