Merge branch 'master' into fixTorando

This commit is contained in:
0xZick 地方分権化 2021-07-16 13:30:07 +03:00 committed by GitHub
commit 4dbdd42252
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 139 additions and 72 deletions

7
.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"semi": false,
"printWidth": 130
}

View File

@ -7,24 +7,25 @@ Current cli version doesn't support [Anonymity Mining](https://tornado-cash.medi
Example:
```bash
./cli.js deposit ETH 0.1 --rpc https://kovan.infura.io/v3/27a9649f826b4e31a83e07ae09a87448
$ ./cli.js deposit ETH 0.1 --rpc https://kovan.infura.io/v3/27a9649f826b4e31a83e07ae09a87448
Your note: tornado-eth-0.1-42-0xf73dd6833ccbcc046c44228c8e2aa312bf49e08389dadc7c65e6a73239867b7ef49c705c4db227e2fadd8489a494b6880bdcb6016047e019d1abec1c7652
Tornado ETH balance is 8.9
Sender account ETH balance is 1004873.470619891361352542
Submitting deposit transaction
Tornado ETH balance is 9
Sender account ETH balance is 1004873.361652048361352542
```
> Your note: tornado-eth-0.1-42-0xf73dd6833ccbcc046c44228c8e2aa312bf49e08389dadc7c65e6a73239867b7ef49c705c4db227e2fadd8489a494b6880bdcb6016047e019d1abec1c7652
> Tornado ETH balance is 8.9
> Sender account ETH balance is 1004873.470619891361352542
> Submitting deposit transaction
> Tornado ETH balance is 9
> Sender account ETH balance is 1004873.361652048361352542
```bash
./cli.js withdraw tornado-eth-0.1-42-0xf73dd6833ccbcc046c44228c8e2aa312bf49e08389dadc7c65e6a73239867b7ef49c705c4db227e2fadd8489a494b6880bdcb6016047e019d1abec1c7652 0x8589427373D6D84E98730D7795D8f6f8731FDA16 --rpc https://kovan.infura.io/v3/27a9649f826b4e31a83e07ae09a87448 --relayer https://kovan-frelay.duckdns.org
```
$ ./cli.js withdraw tornado-eth-0.1-42-0xf73dd6833ccbcc046c44228c8e2aa312bf49e08389dadc7c65e6a73239867b7ef49c705c4db227e2fadd8489a494b6880bdcb6016047e019d1abec1c7652 0x8589427373D6D84E98730D7795D8f6f8731FDA16 --rpc https://kovan.infura.io/v3/27a9649f826b4e31a83e07ae09a87448 --relayer https://kovan-frelay.duckdns.org
> Relay address: 0x6A31736e7490AbE5D5676be059DFf064AB4aC754
> Getting current state from tornado contract
> Generating SNARK proof
> Proof time: 9117.051ms
> Sending withdraw transaction through relay
> Transaction submitted through the relay. View transaction on etherscan https://kovan.etherscan.io/tx/0xcb21ae8cad723818c6bc7273e83e00c8393fcdbe74802ce5d562acad691a2a7b
> Transaction mined in block 17036120
> Done
Relay address: 0x6A31736e7490AbE5D5676be059DFf064AB4aC754
Getting current state from tornado contract
Generating SNARK proof
Proof time: 9117.051ms
Sending withdraw transaction through relay
Transaction submitted through the relay. View transaction on etherscan https://kovan.etherscan.io/tx/0xcb21ae8cad723818c6bc7273e83e00c8393fcdbe74802ce5d562acad691a2a7b
Transaction mined in block 17036120
Done
```

137
cli.js
View File

@ -23,14 +23,14 @@ let web3, tornado, mixerContract, tornadoInstance, circuit, proving_key, groth16
let MERKLE_TREE_HEIGHT, ETH_AMOUNT, TOKEN_AMOUNT, PRIVATE_KEY
/** Whether we are in a browser or node.js */
const inBrowser = (typeof window !== 'undefined')
const inBrowser = typeof window !== 'undefined'
let isLocalRPC = false
/** Generate random number of specified byte length */
const rbigint = nbytes => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes))
const rbigint = (nbytes) => snarkjs.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 */
function toHex(number, length = 32) {
@ -69,7 +69,10 @@ function createDeposit({ nullifier, secret }) {
* @param amount Deposit amount
*/
async function deposit({ currency, amount }) {
const deposit = createDeposit({ nullifier: rbigint(31), secret: rbigint(31) })
const deposit = createDeposit({
nullifier: rbigint(31),
secret: rbigint(31)
})
const note = toHex(deposit.preimage, 62)
const noteString = `tornado-${currency}-${amount}-${netId}-${note}`
console.log(`Your note: ${noteString}`)
@ -81,7 +84,8 @@ async function deposit({ currency, amount }) {
await tornado.methods.deposit(tornadoInstance, toHex(deposit.commitment), []).send({ value, from: senderAccount, gas: 2e6 })
await printETHBalance({ address: tornado._address, name: 'Tornado' })
await printETHBalance({ address: senderAccount, name: 'Sender account' })
} else { // a token
} else {
// a token
await printERC20Balance({ address: tornado._address, name: 'Tornado' })
await printERC20Balance({ address: senderAccount, name: 'Sender account' })
const decimals = isLocalRPC ? 18 : config.deployments[`netId${netId}`][currency].decimals
@ -116,6 +120,7 @@ async function deposit({ currency, amount }) {
async function generateMerkleProof(deposit, amount) {
let leafIndex = -1
// Get all deposit events from smart contract and assemble merkle tree from them
const cachedEvents = loadCachedEvents({ type: 'Deposit', amount })
const startBlock = cachedEvents.lastBlock
@ -189,7 +194,7 @@ async function generateProof({ deposit, amount, recipient, relayerAddress = 0, f
nullifier: deposit.nullifier,
secret: deposit.secret,
pathElements: path_elements,
pathIndices: path_index,
pathIndices: path_index
}
console.log('Generating SNARK proof')
@ -227,13 +232,21 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, refu
const relayerStatus = await axios.get(relayerURL + '/status')
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')
console.log('Relay address: ', rewardAccount)
const gasPrice = await fetchGasPrice()
const decimals = isLocalRPC ? 18 : config.deployments[`netId${netId}`][currency].decimals
const fee = calculateFee({ currency, gasPrice, amount, refund, ethPrices, relayerServiceFee: tornadoServiceFee, decimals })
const fee = calculateFee({
currency,
gasPrice,
amount,
refund,
ethPrices,
relayerServiceFee: tornadoServiceFee,
decimals
})
if (fee.gt(fromDecimals({ amount, decimals }))) {
throw new Error('Too high refund')
}
@ -242,7 +255,11 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, refu
console.log('Sending withdraw transaction through relay')
try {
const response = await axios.post(relayerURL + '/v1/tornadoWithdraw', { contract: tornadoInstance, proof, args })
const response = await axios.post(relayerURL + '/v1/tornadoWithdraw', {
contract: tornadoInstance,
proof,
args
})
const { id } = response.data
@ -255,25 +272,29 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, refu
console.error(e.message)
}
}
} else { // using private key
} else {
// using private key
const { proof, args } = await generateProof({ deposit, recipient, refund })
console.log('Submitting withdraw transaction')
await tornado.methods.withdraw(tornadoInstance, proof, ...args).send({ from: senderAccount, value: refund.toString(), gas: 1e6 })
await tornado.methods
.withdraw(tornadoInstance, proof, ...args)
.send({ from: senderAccount, value: refund.toString(), gas: 1e6 })
.on('transactionHash', function (txHash) {
if (netId === 1 || netId === 42) {
console.log(`View transaction on etherscan https://${getCurrentNetworkName()}etherscan.io/tx/${txHash}`)
} else {
console.log(`The transaction hash is ${txHash}`)
}
}).on('error', function (e) {
})
.on('error', function (e) {
console.error('on transactionHash error', e.message)
})
}
console.log('Done')
}
function getStatus (id, relayerURL) {
function getStatus(id, relayerURL) {
return new Promise((resolve) => {
async function getRelayerStatus() {
const responseStatus = await axios.get(relayerURL + '/v1/jobs/' + id)
@ -289,7 +310,9 @@ function getStatus (id, relayerURL) {
if (status === 'CONFIRMED') {
const receipt = await waitForTxReceipt({ txHash })
console.log(`Transaction submitted through the relay. View transaction on etherscan https://${getCurrentNetworkName()}etherscan.io/tx/${txHash}`)
console.log(
`Transaction submitted through the relay. View transaction on etherscan https://${getCurrentNetworkName()}etherscan.io/tx/${txHash}`
)
console.log('Transaction mined in block', receipt.blockNumber)
resolve(status)
}
@ -322,9 +345,7 @@ function fromDecimals({ amount, decimals }) {
// Split it into a whole and fractional part
const comps = ether.split('.')
if (comps.length > 2) {
throw new Error(
'[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal points'
)
throw new Error('[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal points')
}
let whole = comps[0]
@ -337,9 +358,7 @@ function fromDecimals({ amount, decimals }) {
fraction = '0'
}
if (fraction.length > baseLength) {
throw new Error(
'[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal places'
)
throw new Error('[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal places')
}
while (fraction.length < baseLength) {
@ -401,7 +420,6 @@ function getCurrentNetworkName() {
case 42:
return 'kovan.'
}
}
function gasPrices(value = 80) {
@ -423,9 +441,8 @@ async function fetchGasPrice() {
}
function calculateFee({ currency, gasPrice, amount, refund, ethPrices, relayerServiceFee, decimals }) {
const decimalsPoint = Math.floor(relayerServiceFee) === Number(relayerServiceFee) ?
0 :
relayerServiceFee.toString().split('.')[1].length
const decimalsPoint =
Math.floor(relayerServiceFee) === Number(relayerServiceFee) ? 0 : relayerServiceFee.toString().split('.')[1].length
const roundDecimal = 10 ** decimalsPoint
const total = toBN(fromDecimals({ amount, decimals }))
const feePercent = total.mul(toBN(relayerServiceFee * roundDecimal)).div(toBN(roundDecimal * 100))
@ -437,7 +454,8 @@ function calculateFee({ currency, gasPrice, amount, refund, ethPrices, relayerSe
break
}
default: {
desiredFee = expense.add(toBN(refund))
desiredFee = expense
.add(toBN(refund))
.mul(toBN(10 ** decimals))
.div(toBN(ethPrices[currency]))
desiredFee = desiredFee.add(feePercent)
@ -512,7 +530,12 @@ function parseNote(noteString) {
const deposit = createDeposit({ nullifier, secret })
const netId = Number(match.groups.netId)
return { currency: match.groups.currency, amount: match.groups.amount, netId, deposit }
return {
currency: match.groups.currency,
amount: match.groups.amount,
netId,
deposit
}
}
async function loadDepositData({ deposit }) {
@ -533,7 +556,13 @@ async function loadDepositData({ deposit }) {
const isSpent = await tornado.methods.isSpent(deposit.nullifierHex).call()
const receipt = await web3.eth.getTransactionReceipt(txHash)
return { timestamp, txHash, isSpent, from: receipt.from, commitment: deposit.commitmentHex }
return {
timestamp,
txHash,
isSpent,
from: receipt.from,
commitment: deposit.commitmentHex
}
} catch (e) {
console.error('loadDepositData', e)
}
@ -569,9 +598,7 @@ async function loadWithdrawalData({ amount, currency, deposit }) {
const fee = withdrawEvent.fee
const decimals = config.deployments[`netId${netId}`][currency].decimals
const withdrawalAmount = toBN(fromDecimals({ amount, decimals })).sub(
toBN(fee)
)
const withdrawalAmount = toBN(fromDecimals({ amount, decimals })).sub(toBN(fee))
const { timestamp } = await web3.eth.getBlock(withdrawEvent.blockHash)
return {
amount: toDecimals(withdrawalAmount, decimals, 9),
@ -595,7 +622,9 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100' }) {
if (inBrowser) {
// Initialize using injected web3 (Metamask)
// To assemble web version run `npm run browserify`
web3 = new Web3(window.web3.currentProvider, null, { transactionConfirmationBlocks: 1 })
web3 = new Web3(window.web3.currentProvider, null, {
transactionConfirmationBlocks: 1
})
contractJson = await (await fetch('build/contracts/TornadoProxy.abi.json')).json()
mixerJson = await (await fetch('build/contracts/Mixer.abi.json')).json()
circuit = await (await fetch('build/circuits/tornado.json')).json()
@ -678,7 +707,9 @@ async function main() {
.option('-R, --relayer <URL>', 'Withdraw via relayer')
program
.command('deposit <currency> <amount>')
.description('Submit a deposit of specified currency and amount from default eth account and return the resulting note. The currency is one of (ETH|DAI|cDAI|USDC|cUSDC|USDT). The amount depends on currency, see config.js file or visit https://tornado.cash.')
.description(
'Submit a deposit of specified currency and amount from default eth account and return the resulting note. The currency is one of (ETH|DAI|cDAI|USDC|cUSDC|USDT). The amount depends on currency, see config.js file or visit https://tornado.cash.'
)
.action(async (currency, amount) => {
currency = currency.toLowerCase()
await init({ rpc: program.rpc, currency, amount })
@ -686,11 +717,20 @@ async function main() {
})
program
.command('withdraw <note> <recipient> [ETH_purchase]')
.description('Withdraw a note to a recipient account using relayer or specified private key. You can exchange some of your deposit`s tokens to ETH during the withdrawal by specifing ETH_purchase (e.g. 0.01) to pay for gas in future transactions. Also see the --relayer option.')
.description(
'Withdraw a note to a recipient account using relayer or specified private key. You can exchange some of your deposit`s tokens to ETH during the withdrawal by specifing ETH_purchase (e.g. 0.01) to pay for gas in future transactions. Also see the --relayer option.'
)
.action(async (noteString, recipient, refund) => {
const { currency, amount, netId, deposit } = parseNote(noteString)
await init({ rpc: program.rpc, noteNetId: netId, currency, amount })
await withdraw({ deposit, currency, amount, recipient, refund, relayerURL: program.relayer })
await withdraw({
deposit,
currency,
amount,
recipient,
refund,
relayerURL: program.relayer
})
})
program
.command('balance <address> [token_address]')
@ -704,7 +744,9 @@ async function main() {
})
program
.command('compliance <note>')
.description('Shows the deposit and withdrawal of the provided note. This might be necessary to show the origin of assets held in your withdrawal address.')
.description(
'Shows the deposit and withdrawal of the provided note. This might be necessary to show the origin of assets held in your withdrawal address.'
)
.action(async (noteString) => {
const { currency, amount, netId, deposit } = parseNote(noteString)
await init({ rpc: program.rpc, noteNetId: netId, currency, amount })
@ -720,7 +762,11 @@ async function main() {
console.log('The note was not spent')
}
const withdrawInfo = await loadWithdrawalData({ amount, currency, deposit })
const withdrawInfo = await loadWithdrawalData({
amount,
currency,
deposit
})
const withdrawalDate = new Date(withdrawInfo.timestamp * 1000)
console.log('\n=============Withdrawal==============')
console.log('Withdrawal :', withdrawInfo.amount, currency)
@ -740,15 +786,28 @@ async function main() {
await init({ rpc: program.rpc, currency, amount })
let noteString = await deposit({ currency, amount })
let parsedNote = parseNote(noteString)
await withdraw({ deposit: parsedNote.deposit, currency, amount, recipient: senderAccount, relayerURL: program.relayer })
await withdraw({
deposit: parsedNote.deposit,
currency,
amount,
recipient: senderAccount,
relayerURL: program.relayer
})
console.log('\nStart performing DAI deposit-withdraw test')
currency = 'dai'
amount = '100'
await init({ rpc: program.rpc, currency, amount })
noteString = await deposit({ currency, amount })
; (parsedNote = parseNote(noteString))
await withdraw({ deposit: parsedNote.deposit, currency, amount, recipient: senderAccount, refund: '0.02', relayerURL: program.relayer })
parsedNote = parseNote(noteString)
await withdraw({
deposit: parsedNote.deposit,
currency,
amount,
recipient: senderAccount,
refund: '0.02',
relayerURL: program.relayer
})
})
try {
await program.parseAsync(process.argv)