Compare commits

..

No commits in common. "master" and "proposal-29" have entirely different histories.

4 changed files with 739 additions and 748 deletions

119
README.md

@ -2,8 +2,10 @@
Command line tool to interact with [Tornado Cash](https://tornado.ws). Command line tool to interact with [Tornado Cash](https://tornado.ws).
### How to install tornado cli ### Warning!
Current cli version doesn't support [Anonymity Mining](https://tornado-cash.medium.com/tornado-cash-governance-proposal-a55c5c7d0703)
### How to install tornado cli
Download and install [node.js](https://nodejs.org/en/download/). Download and install [node.js](https://nodejs.org/en/download/).
You also need to install C++ build tools in order to do 'npm install', for more information please checkout https://github.com/nodejs/node-gyp#on-unix. You also need to install C++ build tools in order to do 'npm install', for more information please checkout https://github.com/nodejs/node-gyp#on-unix.
@ -34,7 +36,6 @@ If you want to use Tor connection to conceal ip address, install [Tor Browser](h
Note that you should reset your tor connection by restarting the browser every time when you deposit & withdraw otherwise you will have the same exit node used for connection. Note that you should reset your tor connection by restarting the browser every time when you deposit & withdraw otherwise you will have the same exit node used for connection.
### Goerli, Mainnet, Binance Smart Chain, Gnosis Chain, Polygon Network, Arbitrum, Avalanche ### Goerli, Mainnet, Binance Smart Chain, Gnosis Chain, Polygon Network, Arbitrum, Avalanche
1. `node cli.js --help` 1. `node cli.js --help`
2. If you want to use secure, anonymous tor connection add `--tor <torPort>` behind the command. 2. If you want to use secure, anonymous tor connection add `--tor <torPort>` behind the command.
3. Add `PRIVATE_KEY` to `.env` file (optional, only if you want to use it for many operations) - open `.env.example` file, add private key after `PRIVATE_KEY=` and rename file to `.env`. 3. Add `PRIVATE_KEY` to `.env` file (optional, only if you want to use it for many operations) - open `.env.example` file, add private key after `PRIVATE_KEY=` and rename file to `.env`.
@ -50,7 +51,6 @@ Note that `--tor <torPort>` is optional, and use `--private-key <private key>` o
For RPC nodes please refer to the list of public RPC nodes below. For RPC nodes please refer to the list of public RPC nodes below.
##### Example: ##### Example:
```bash ```bash
$ node cli.js deposit ETH 0.1 --rpc https://mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607 --tor 9150 $ node cli.js deposit ETH 0.1 --rpc https://mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607 --tor 9150
@ -91,7 +91,6 @@ Done
``` ```
### (Optional) Creating Deposit Notes & Invoices offline ### (Optional) Creating Deposit Notes & Invoices offline
One of the main features of tornado-cli is that it supports creating deposit notes & invoices inside the offline computing environment. One of the main features of tornado-cli is that it supports creating deposit notes & invoices inside the offline computing environment.
After the private-key like notes are backed up somewhere safe, you can copy the created deposit invoices and use them to create new deposit transaction on online environment. After the private-key like notes are backed up somewhere safe, you can copy the created deposit invoices and use them to create new deposit transaction on online environment.
@ -145,13 +144,13 @@ Sender account balance is x.xxxxxxx ETH
```json ```json
{ {
"netId1": { "netId1":{
"rpcUrls": { "rpcUrls":{
"publicRpc1": { "publicRpc1":{
"name": "1RPC", "name":"1RPC",
"url": "https://1rpc.io/eth" "url":"https://1rpc.io/eth"
}, },
"Chainnodes": { "Chainnodes":{
"name": "Chainnodes", "name": "Chainnodes",
"url": "https://mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607" "url": "https://mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607"
} }
@ -249,20 +248,20 @@ Sender account balance is x.xxxxxxx ETH
} }
} }
}, },
"netId56": { "netId56":{
"rpcUrls": { "rpcUrls":{
"publicRpc1": { "publicRpc1":{
"name": "BSC Public RPC 1", "name":"BSC Public RPC 1",
"url": "https://1rpc.io/bnb" "url":"https://1rpc.io/bnb"
}, },
"publicRpc2": { "publicRpc2":{
"name": "BSC Public RPC 2", "name":"BSC Public RPC 2",
"url": "https://bsc-dataseed1.defibit.io" "url":"https://bsc-dataseed1.defibit.io"
},
"publicRpc3":{
"name":"BSC Public RPC 3",
"url":"https://bsc-dataseed1.ninicoin.io"
}, },
"publicRpc3": {
"name": "BSC Public RPC 3",
"url": "https://bsc-dataseed1.ninicoin.io"
}
}, },
"relayers": { "relayers": {
"0xproxy.eth": { "0xproxy.eth": {
@ -337,14 +336,14 @@ Sender account balance is x.xxxxxxx ETH
} }
} }
}, },
"netId100": { "netId100":{
"rpcUrls": { "rpcUrls":{
"publicRpc": { "publicRpc":{
"name": "Gnosis Chain RPC", "name":"Gnosis Chain RPC",
"url": "https://rpc.gnosischain.com" "url":"https://rpc.gnosischain.com"
} }
}, },
"relayers": { "relayers":{
"torndao.eth": { "torndao.eth": {
"url": "torndao.eth", "url": "torndao.eth",
"name": "torndao.eth", "name": "torndao.eth",
@ -352,13 +351,13 @@ Sender account balance is x.xxxxxxx ETH
} }
} }
}, },
"netId137": { "netId137":{
"rpcUrls": { "rpcUrls":{
"publicRpc1": { "publicRpc1":{
"name": "1RPC", "name":"1RPC",
"url": "https://1rpc.io/matic" "url":"https://1rpc.io/matic"
}, },
"Chainnodes": { "Chainnodes":{
"name": "Chainnodes", "name": "Chainnodes",
"url": "https://polygon-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607" "url": "https://polygon-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607"
} }
@ -406,18 +405,18 @@ Sender account balance is x.xxxxxxx ETH
} }
} }
}, },
"netId42161": { "netId42161":{
"rpcUrls": { "rpcUrls":{
"publicRpc1": { "publicRpc1":{
"name": "Arbitrum Public RPC", "name":"Arbitrum Public RPC",
"url": "https://arb1.arbitrum.io/rpc" "url":"https://arb1.arbitrum.io/rpc"
}, },
"publicRpc2": { "publicRpc2":{
"name": "ChainnodesRPC", "name": "ChainnodesRPC",
"url": "https://arbitrum-one.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607" "url": "https://arbitrum-one.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607"
} }
}, },
"relayers": { "relayers":{
"tornado-crypto-bot-exchange.eth": { "tornado-crypto-bot-exchange.eth": {
"url": "tornado-crypto-bot-exchange.eth", "url": "tornado-crypto-bot-exchange.eth",
"name": "tornado-crypto-bot-exchange.eth", "name": "tornado-crypto-bot-exchange.eth",
@ -430,14 +429,14 @@ Sender account balance is x.xxxxxxx ETH
} }
} }
}, },
"netId43114": { "netId43114":{
"rpcUrls": { "rpcUrls":{
"publicRpc": { "publicRpc":{
"name": "1RPC", "name":"1RPC",
"url": "https://1rpc.io/avax/c" "url":"https://1rpc.io/avax/c"
} }
}, },
"relayers": { "relayers":{
"cheap-relayer.eth": { "cheap-relayer.eth": {
"url": "cheap-relayer.eth", "url": "cheap-relayer.eth",
"name": "cheap-relayer.eth", "name": "cheap-relayer.eth",
@ -460,18 +459,18 @@ Sender account balance is x.xxxxxxx ETH
} }
} }
}, },
"netId10": { "netId10":{
"rpcUrls": { "rpcUrls":{
"publicRpc1": { "publicRpc1":{
"name": "1RPC", "name":"1RPC",
"url": "https://1rpc.io/op" "url":"https://1rpc.io/op"
}, },
"Chainnodes": { "Chainnodes":{
"name": "Chainnodes", "name": "Chainnodes",
"url": "https://optimism-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607" "url": "https://optimism-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607"
} }
}, },
"relayers": { "relayers":{
"tornado-crypto-bot-exchange.eth": { "tornado-crypto-bot-exchange.eth": {
"url": "tornado-crypto-bot-exchange.eth", "url": "tornado-crypto-bot-exchange.eth",
"name": "tornado-crypto-bot-exchange.eth", "name": "tornado-crypto-bot-exchange.eth",
@ -479,14 +478,14 @@ Sender account balance is x.xxxxxxx ETH
} }
} }
}, },
"netId5": { "netId5":{
"rpcUrls": { "rpcUrls":{
"Chainnodes": { "Chainnodes":{
"name": "Chainnodes RPC", "name":"Chainnodes RPC",
"url": "https://goerli.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607" "url":"https://goerli.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607"
} }
}, },
"relayers": {} "relayers":{}
} }
} }
``` ```

22
cli.js

@ -10,13 +10,14 @@ const circomlib = require('@tornado/circomlib');
const bigInt = snarkjs.bigInt; const bigInt = snarkjs.bigInt;
const merkleTree = require('@tornado/fixed-merkle-tree'); const merkleTree = require('@tornado/fixed-merkle-tree');
const Web3 = require('web3'); const Web3 = require('web3');
const Web3HttpProvider = require('@tornado/web3-providers-http');
const buildGroth16 = require('@tornado/websnark/src/groth16'); const buildGroth16 = require('@tornado/websnark/src/groth16');
const websnarkUtils = require('@tornado/websnark/src/utils'); const websnarkUtils = require('@tornado/websnark/src/utils');
const { toWei, fromWei, toBN, BN } = require('web3-utils'); const { toWei, fromWei, toBN, BN } = require('web3-utils');
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
const config = require('./config'); const config = require('./config');
const program = require('commander'); const program = require('commander');
const { TornadoFeeOracleV4, TornadoFeeOracleV5 } = require('@tornado/tornado-oracles'); const { TornadoFeeOracleV4, TornadoFeeOracleV5, TokenPriceOracle } = require('@tornado/tornado-oracles');
const { SocksProxyAgent } = require('socks-proxy-agent'); const { SocksProxyAgent } = require('socks-proxy-agent');
const is_ip_private = require('private-ip'); const is_ip_private = require('private-ip');
const readline = require('readline'); const readline = require('readline');
@ -485,15 +486,7 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, refu
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' }
}; };
} }
const relayerStatus = await axios.get(relayerURL + '/status', options);
let relayerStatus;
try {
relayerURL = new URL(relayerURL).origin;
relayerStatus = await axios.get(relayerURL + '/status', options);
} catch (err) {
console.error(err);
throw new Error('Cannot get relayer status');
}
const { rewardAccount, netId, ethPrices, tornadoServiceFee } = relayerStatus.data; const { rewardAccount, netId, ethPrices, tornadoServiceFee } = relayerStatus.data;
assert(netId === (await web3.eth.net.getId()) || netId === '*', 'This relay is for different network'); assert(netId === (await web3.eth.net.getId()) || netId === '*', 'This relay is for different network');
@ -1366,7 +1359,7 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', balanceC
console.log('Using tor network'); console.log('Using tor network');
web3Options = { agent: { https: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort) }, timeout: 60000 }; web3Options = { agent: { https: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort) }, timeout: 60000 };
// Use forked web3-providers-http from local file to modify user-agent header value which improves privacy. // Use forked web3-providers-http from local file to modify user-agent header value which improves privacy.
web3 = new Web3(new Web3.providers.HttpProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 }); web3 = new Web3(new Web3HttpProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 });
ipOptions = { ipOptions = {
httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort), httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort),
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' }
@ -1375,22 +1368,21 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', balanceC
console.log('Using tor network'); console.log('Using tor network');
web3Options = { agent: { http: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort) }, timeout: 60000 }; web3Options = { agent: { http: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort) }, timeout: 60000 };
// Use forked web3-providers-http from local file to modify user-agent header value which improves privacy. // Use forked web3-providers-http from local file to modify user-agent header value which improves privacy.
web3 = new Web3(new Web3.providers.HttpProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 }); web3 = new Web3(new Web3HttpProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 });
ipOptions = { ipOptions = {
httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort), httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort),
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' }
}; };
} else if (rpc.includes('ipc')) { } else if (rpc.includes('ipc')) {
console.log('Using ipc connection'); console.log('Using ipc connection');
web3 = new Web3(new Web3.providers.IpcProvider(rpc, {}), null, { transactionConfirmationBlocks: 1 }); web3 = new Web3(new Web3.providers.IpcProvider(rpc, net), null, { transactionConfirmationBlocks: 1 });
} else if (rpc.startsWith('ws') || rpc.startsWith('wss')) { } else if (rpc.startsWith('ws') || rpc.startsWith('wss')) {
console.log('Using websocket connection (Note: Tor is not supported for Websocket providers)'); console.log('Using websocket connection (Note: Tor is not supported for Websocket providers)');
web3Options = { web3Options = {
clientConfig: { keepalive: true, keepaliveInterval: -1 }, clientConfig: { keepalive: true, keepaliveInterval: -1 },
reconnect: { auto: true, delay: 1000, maxAttempts: 10, onTimeout: false } reconnect: { auto: true, delay: 1000, maxAttempts: 10, onTimeout: false }
}; };
web3 = new Web3(new Web3.providers.WebsocketProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 }); web3 = new Web3(new Web3.providers.WebsocketProvider(rpc, web3Options), net, { transactionConfirmationBlocks: 1 });
if (!(await web3.eth.net.isListening())) throw new Error('Cannot connect to websocket provider');
} else { } else {
console.log('Connecting to remote node'); console.log('Connecting to remote node');
web3 = new Web3(rpc, null, { transactionConfirmationBlocks: 1 }); web3 = new Web3(rpc, null, { transactionConfirmationBlocks: 1 });

751
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -11,11 +11,12 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@ethersproject/transactions": "^5.7.0", "@ethersproject/transactions": "^5.7.0",
"@tornado/circomlib": "0.0.21", "@tornado/circomlib": "0.0.20-p2",
"@tornado/fixed-merkle-tree": "0.6.1", "@tornado/fixed-merkle-tree": "0.6.1-p1",
"@tornado/snarkjs": "0.1.20", "@tornado/snarkjs": "0.1.20-p2",
"@tornado/tornado-oracles": "^3.3.0", "@tornado/tornado-oracles": "^3.3.0",
"@tornado/websnark": "0.0.4", "@tornado/web3-providers-http": "1.6.5-p1",
"@tornado/websnark": "0.0.4-p1",
"axios": "^0.19.2", "axios": "^0.19.2",
"bignumber.js": "^9.0.0", "bignumber.js": "^9.0.0",
"circom": "0.0.35", "circom": "0.0.35",
@ -24,7 +25,7 @@
"es5-ext": "0.10.53", "es5-ext": "0.10.53",
"private-ip": "^2.3.3", "private-ip": "^2.3.3",
"socks-proxy-agent": "^6.1.1", "socks-proxy-agent": "^6.1.1",
"web3": "^1.10.2" "web3": "^1.6.1"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^7.0.0" "eslint": "^7.0.0"