diff --git a/cli.js b/cli.js index fdd0f16..53ee9bb 100755 --- a/cli.js +++ b/cli.js @@ -3,41 +3,23 @@ // Works both in browser and node.js require('dotenv').config() -const fs = require('fs') + const axios = require('axios') const assert = require('assert') -const snarkjs = require('snarkjs') -const crypto = require('crypto') -const circomlib = require('circomlib') -const bigInt = snarkjs.bigInt -const merkleTree = require('./lib/MerkleTree') const Web3 = require('web3') -const buildGroth16 = require('websnark/src/groth16') -const websnarkUtils = require('websnark/src/utils') + const { toWei, fromWei, toBN, BN } = require('web3-utils') const config = require('./config') const program = require('commander') const { GasPriceOracle } = require('gas-price-oracle') -let web3, tornado, mixerContract, tornadoInstance, circuit, proving_key, groth16, erc20, senderAccount, netId +const { initialize, createDeposit, generateProof, toHex, rbigint, bigInt } = require('./core') + +let web3, tornado, mixerContract, tornadoInstance, erc20, senderAccount, netId let MERKLE_TREE_HEIGHT, ETH_AMOUNT, TOKEN_AMOUNT, PRIVATE_KEY -/** Whether we are in a browser or node.js */ -const inBrowser = typeof window !== 'undefined' let isLocalRPC = false -/** Generate random number of specified byte length */ -const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes)) - -/** Compute pedersen hash */ -const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0] - -/** BigNumber to hex string of specified length */ -function toHex(number, length = 32) { - const str = number instanceof Buffer ? number.toString('hex') : bigInt(number).toString(16) - return '0x' + str.padStart(length * 2, '0') -} - /** Display ETH account balance */ async function printETHBalance({ address, name }) { console.log(`${name} ETH balance is`, web3.utils.fromWei(await web3.eth.getBalance(address))) @@ -50,19 +32,6 @@ async function printERC20Balance({ address, name, tokenAddress }) { console.log(`${name} Token Balance is`, web3.utils.fromWei(await erc20.methods.balanceOf(address).call())) } -/** - * Create deposit object from secret and nullifier - */ -function createDeposit({ nullifier, secret }) { - const deposit = { nullifier, secret } - deposit.preimage = Buffer.concat([deposit.nullifier.leInt2Buff(31), deposit.secret.leInt2Buff(31)]) - deposit.commitment = pedersenHash(deposit.preimage) - deposit.commitmentHex = toHex(deposit.commitment) - deposit.nullifierHash = pedersenHash(deposit.nullifier.leInt2Buff(31)) - deposit.nullifierHex = toHex(deposit.nullifierHash) - return deposit -} - /** * Make a deposit * @param currency Сurrency @@ -111,97 +80,17 @@ async function deposit({ currency, amount }) { return noteString } -/** - * Generate merkle tree for a deposit. - * Download deposit events from the tornado, reconstructs merkle tree, finds our deposit leaf - * in it and generates merkle proof - * @param deposit Deposit object - */ -async function generateMerkleProof(deposit) { - let leafIndex = -1 - // Get all deposit events from smart contract and assemble merkle tree from them - const events = await mixerContract.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) => { - const index = toBN(e.returnValues.leafIndex).toNumber() - - if (toBN(e.returnValues.commitment).eq(toBN(deposit.commitmentHex))) { - leafIndex = index - } - return e.returnValues.commitment.toString(10) - }) - const tree = new merkleTree(MERKLE_TREE_HEIGHT, leaves) - - // Validate that our data is correct - const root = await tree.root() - const isValidRoot = await mixerContract.methods.isKnownRoot(toHex(root)).call() - const isSpent = await mixerContract.methods.isSpent(toHex(deposit.nullifierHash)).call() - assert(isValidRoot === true, 'Merkle tree is corrupted') - assert(isSpent === false, 'The note is already spent') - assert(leafIndex >= 0, 'The deposit is not found in the tree') - - // Compute merkle proof of our commitment - return tree.path(leafIndex) -} - -/** - * Generate SNARK proof for withdrawal - * @param deposit Deposit object - * @param recipient Funds recipient - * @param relayer Relayer address - * @param fee Relayer fee - * @param refund Receive ether for exchanged tokens - */ -async function generateProof({ deposit, recipient, relayerAddress = 0, fee = 0, refund = 0 }) { - // Compute merkle proof of our commitment - const { root, path_elements, path_index } = await generateMerkleProof(deposit) - - // Prepare circuit input - const input = { - // Public snark inputs - root: root, - nullifierHash: deposit.nullifierHash, - recipient: bigInt(recipient), - relayer: bigInt(relayerAddress), - fee: bigInt(fee), - refund: bigInt(refund), - - // Private snark inputs - nullifier: deposit.nullifier, - secret: deposit.secret, - pathElements: path_elements, - pathIndices: path_index - } - - console.log('Generating SNARK proof') - console.time('Proof time') - const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) - const { proof } = websnarkUtils.toSolidityInput(proofData) - console.timeEnd('Proof time') - - const args = [ - toHex(input.root), - toHex(input.nullifierHash), - toHex(input.recipient, 20), - toHex(input.relayer, 20), - toHex(input.fee), - toHex(input.refund) - ] - - return { proof, args } -} - /** * Do an ETH withdrawal * @param noteString Note to withdraw * @param recipient Recipient address */ async function withdraw({ deposit, currency, amount, recipient, relayerURL, refund = '0' }) { + // Get all deposit events from smart contract and assemble merkle tree from them + const events = await mixerContract.getPastEvents('Deposit', { + fromBlock: 0, + toBlock: 'latest' + }) if (currency === 'eth' && refund !== '0') { throw new Error('The ETH purchase is supposted to be 0 for ETH withdrawals') } @@ -237,9 +126,17 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, refu recipient, relayerAddress: rewardAccount, fee, - refund + refund, + events }) + // Validate that our data is correct + // const isValidRoot = await mixerContract.methods.isKnownRoot(toHex(root)).call() + // const isSpent = await mixerContract.methods.isSpent(toHex(deposit.nullifierHash)).call() + // assert(isValidRoot === true, 'Merkle tree is corrupted') + // assert(isSpent === false, 'The note is already spent') + // assert(leafIndex >= 0, 'The deposit is not found in the tree') + console.log('Sending withdraw transaction through relay') try { const response = await axios.post(relayerURL + '/v1/tornadoWithdraw', { @@ -261,7 +158,7 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, refu } } else { // using private key - const { proof, args } = await generateProof({ deposit, recipient, refund }) + const { proof, args } = await generateProof({ deposit, recipient, refund, events }) console.log('Submitting withdraw transaction') await tornado.methods @@ -564,45 +461,27 @@ async function loadWithdrawalData({ amount, currency, deposit }) { */ async function init({ rpc, noteNetId, currency = 'dai', amount = '100' }) { let contractJson, mixerJson, erc20ContractJson, erc20tornadoJson, tornadoAddress, tokenAddress - // TODO do we need this? should it work in browser really? - if (inBrowser) { - // Initialize using injected web3 (Metamask) - // To assemble web version run `npm run browserify` - 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() - proving_key = await (await fetch('build/circuits/tornadoProvingKey.bin')).arrayBuffer() - MERKLE_TREE_HEIGHT = 20 - ETH_AMOUNT = 1e18 - TOKEN_AMOUNT = 1e19 - senderAccount = (await web3.eth.getAccounts())[0] + + web3 = new Web3(rpc, null, { transactionConfirmationBlocks: 1 }) + contractJson = require('./build/contracts/TornadoProxy.abi.json') + mixerJson = require('./build/contracts/Mixer.abi.json') + MERKLE_TREE_HEIGHT = process.env.MERKLE_TREE_HEIGHT || 20 + await initialize({ merkleTreeHeight: MERKLE_TREE_HEIGHT }) + + ETH_AMOUNT = process.env.ETH_AMOUNT + TOKEN_AMOUNT = process.env.TOKEN_AMOUNT + PRIVATE_KEY = process.env.PRIVATE_KEY + if (PRIVATE_KEY) { + const account = web3.eth.accounts.privateKeyToAccount('0x' + PRIVATE_KEY) + web3.eth.accounts.wallet.add('0x' + PRIVATE_KEY) + web3.eth.defaultAccount = account.address + senderAccount = account.address } else { - // Initialize from local node - web3 = new Web3(rpc, null, { transactionConfirmationBlocks: 1 }) - contractJson = require('./build/contracts/TornadoProxy.abi.json') - mixerJson = require('./build/contracts/Mixer.abi.json') - circuit = require('./build/circuits/tornado.json') - proving_key = fs.readFileSync('build/circuits/tornadoProvingKey.bin').buffer - MERKLE_TREE_HEIGHT = process.env.MERKLE_TREE_HEIGHT || 20 - ETH_AMOUNT = process.env.ETH_AMOUNT - TOKEN_AMOUNT = process.env.TOKEN_AMOUNT - PRIVATE_KEY = process.env.PRIVATE_KEY - if (PRIVATE_KEY) { - const account = web3.eth.accounts.privateKeyToAccount('0x' + PRIVATE_KEY) - web3.eth.accounts.wallet.add('0x' + PRIVATE_KEY) - web3.eth.defaultAccount = account.address - senderAccount = account.address - } else { - console.log('Warning! PRIVATE_KEY not found. Please provide PRIVATE_KEY in .env file if you deposit') - } - erc20ContractJson = require('./build/contracts/ERC20Mock.json') - erc20tornadoJson = require('./build/contracts/ERC20Tornado.json') + console.log('Warning! PRIVATE_KEY not found. Please provide PRIVATE_KEY in .env file if you deposit') } - // groth16 initialises a lot of Promises that will never be resolved, that's why we need to use process.exit to terminate the CLI - groth16 = await buildGroth16() + erc20ContractJson = require('./build/contracts/ERC20Mock.json') + erc20tornadoJson = require('./build/contracts/ERC20Tornado.json') + netId = await web3.eth.net.getId() if (noteNetId && Number(noteNetId) !== netId) { throw new Error('This note is for a different network. Specify the --rpc option explicitly') @@ -633,135 +512,119 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100' }) { } async function main() { - if (inBrowser) { - const instance = { currency: 'eth', amount: '0.1' } - await init(instance) - window.deposit = async () => { - await deposit(instance) - } - window.withdraw = async () => { - const noteString = prompt('Enter the note to withdraw') - const recipient = (await web3.eth.getAccounts())[0] - + program + .option('-r, --rpc ', 'The RPC, CLI should interact with', 'http://localhost:8545') + .option('-R, --relayer ', 'Withdraw via relayer') + program + .command('deposit ') + .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 }) + await deposit({ currency, amount }) + }) + program + .command('withdraw [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.' + ) + .action(async (noteString, recipient, refund) => { const { currency, amount, netId, deposit } = parseNote(noteString) - await init({ noteNetId: netId, currency, amount }) - await withdraw({ deposit, currency, amount, recipient }) - } - } else { - program - .option('-r, --rpc ', 'The RPC, CLI should interact with', 'http://localhost:8545') - .option('-R, --relayer ', 'Withdraw via relayer') - program - .command('deposit ') - .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 }) - await deposit({ currency, amount }) + await init({ rpc: program.rpc, noteNetId: netId, currency, amount }) + await withdraw({ + deposit, + currency, + amount, + recipient, + refund, + relayerURL: program.relayer }) - program - .command('withdraw [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.' - ) - .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 - }) - }) - program - .command('balance
[token_address]') - .description('Check ETH and ERC20 balance') - .action(async (address, tokenAddress) => { - await init({ rpc: program.rpc }) - await printETHBalance({ address, name: '' }) - if (tokenAddress) { - await printERC20Balance({ address, name: '', tokenAddress }) - } - }) - program - .command('compliance ') - .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 }) - const depositInfo = await loadDepositData({ deposit }) - const depositDate = new Date(depositInfo.timestamp * 1000) - console.log('\n=============Deposit=================') - console.log('Deposit :', amount, currency) - console.log('Date :', depositDate.toLocaleDateString(), depositDate.toLocaleTimeString()) - console.log('From :', `https://${getCurrentNetworkName()}etherscan.io/address/${depositInfo.from}`) - console.log('Transaction :', `https://${getCurrentNetworkName()}etherscan.io/tx/${depositInfo.txHash}`) - console.log('Commitment :', depositInfo.commitment) - if (deposit.isSpent) { - console.log('The note was not spent') - } + }) + program + .command('balance
[token_address]') + .description('Check ETH and ERC20 balance') + .action(async (address, tokenAddress) => { + await init({ rpc: program.rpc }) + await printETHBalance({ address, name: '' }) + if (tokenAddress) { + await printERC20Balance({ address, name: '', tokenAddress }) + } + }) + program + .command('compliance ') + .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 }) + const depositInfo = await loadDepositData({ deposit }) + const depositDate = new Date(depositInfo.timestamp * 1000) + console.log('\n=============Deposit=================') + console.log('Deposit :', amount, currency) + console.log('Date :', depositDate.toLocaleDateString(), depositDate.toLocaleTimeString()) + console.log('From :', `https://${getCurrentNetworkName()}etherscan.io/address/${depositInfo.from}`) + console.log('Transaction :', `https://${getCurrentNetworkName()}etherscan.io/tx/${depositInfo.txHash}`) + console.log('Commitment :', depositInfo.commitment) + if (deposit.isSpent) { + console.log('The note was not spent') + } - const withdrawInfo = await loadWithdrawalData({ - amount, - currency, - deposit - }) - const withdrawalDate = new Date(withdrawInfo.timestamp * 1000) - console.log('\n=============Withdrawal==============') - console.log('Withdrawal :', withdrawInfo.amount, currency) - console.log('Relayer Fee :', withdrawInfo.fee, currency) - console.log('Date :', withdrawalDate.toLocaleDateString(), withdrawalDate.toLocaleTimeString()) - console.log('To :', `https://${getCurrentNetworkName()}etherscan.io/address/${withdrawInfo.to}`) - console.log('Transaction :', `https://${getCurrentNetworkName()}etherscan.io/tx/${withdrawInfo.txHash}`) - console.log('Nullifier :', withdrawInfo.nullifier) + const withdrawInfo = await loadWithdrawalData({ + amount, + currency, + deposit + }) + const withdrawalDate = new Date(withdrawInfo.timestamp * 1000) + console.log('\n=============Withdrawal==============') + console.log('Withdrawal :', withdrawInfo.amount, currency) + console.log('Relayer Fee :', withdrawInfo.fee, currency) + console.log('Date :', withdrawalDate.toLocaleDateString(), withdrawalDate.toLocaleTimeString()) + console.log('To :', `https://${getCurrentNetworkName()}etherscan.io/address/${withdrawInfo.to}`) + console.log('Transaction :', `https://${getCurrentNetworkName()}etherscan.io/tx/${withdrawInfo.txHash}`) + console.log('Nullifier :', withdrawInfo.nullifier) + }) + program + .command('test') + .description('Perform an automated test. It deposits and withdraws one ETH and one ERC20 note. Uses ganache.') + .action(async () => { + console.log('Start performing ETH deposit-withdraw test') + let currency = 'eth' + let amount = '0.1' + 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 }) - program - .command('test') - .description('Perform an automated test. It deposits and withdraws one ETH and one ERC20 note. Uses ganache.') - .action(async () => { - console.log('Start performing ETH deposit-withdraw test') - let currency = 'eth' - let amount = '0.1' - 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 - }) - 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 - }) + 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 }) - try { - await program.parseAsync(process.argv) - process.exit(0) - } catch (e) { - console.log('Error:', e) - process.exit(1) - } + }) + try { + await program.parseAsync(process.argv) + process.exit(0) + } catch (e) { + console.log('Error:', e) + process.exit(1) } } diff --git a/core.js b/core.js new file mode 100644 index 0000000..9d6a90d --- /dev/null +++ b/core.js @@ -0,0 +1,143 @@ +const fs = require('fs') +const crypto = require('crypto') +const circomlib = require('circomlib') +const websnarkUtils = require('websnark/src/utils') +const MerkleTree = require('fixed-merkle-tree') +const circuit = require('./build/circuits/tornado.json') +const path = require('path') +const proving_key = fs.readFileSync(path.resolve(__dirname, './build/circuits/tornadoProvingKey.bin')).buffer +const buildGroth16 = require('websnark/src/groth16') +const snarkjs = require('snarkjs') +const { toBN } = require('web3-utils') +const bigInt = snarkjs.bigInt +let groth16, MERKLE_TREE_HEIGHT + +/** Generate random number of specified byte length */ +const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes)) + +/** BigNumber to hex string of specified length */ +function toHex(number, length = 32) { + const str = number instanceof Buffer ? number.toString('hex') : bigInt(number).toString(16) + return '0x' + str.padStart(length * 2, '0') +} + +/** Compute pedersen hash */ +const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0] + +/** + * Create deposit object from secret and nullifier + */ +function createDeposit({ nullifier, secret } = {}) { + if (!nullifier && !secret) { + nullifier = rbigint(31) + secret = rbigint(31) + } + const deposit = { nullifier: bigInt(nullifier), secret: bigInt(secret) } + deposit.preimage = Buffer.concat([deposit.nullifier.leInt2Buff(31), deposit.secret.leInt2Buff(31)]) + deposit.commitment = pedersenHash(deposit.preimage) + deposit.commitmentHex = toHex(deposit.commitment) + deposit.nullifierHash = pedersenHash(deposit.nullifier.leInt2Buff(31)) + deposit.nullifierHex = toHex(deposit.nullifierHash) + return deposit +} + +/** + * Generate merkle tree for a deposit. + * @param deposit Deposit object + */ +function generateMerkleProof({ deposit, events }) { + let leafIndex = -1 + + let argsProperty + if (events[0].returnValues) { + argsProperty = 'returnValues' + } else if (events[0].args) { + argsProperty = 'args' + } else { + throw new Error('Only implemented for web3 and ethersjs') + } + + const leaves = events + .sort((a, b) => a[argsProperty].leafIndex - b[argsProperty].leafIndex) // Sort events in chronological order + .map((e) => { + const index = toBN(e[argsProperty].leafIndex).toNumber() + + if (toBN(e[argsProperty].commitment).eq(toBN(deposit.commitmentHex))) { + leafIndex = index + } + return e[argsProperty].commitment.toString(10) + }) + + const tree = new MerkleTree(MERKLE_TREE_HEIGHT, leaves) + + // Compute merkle proof of our commitment + const { pathIndices, pathElements } = tree.path(leafIndex) + return { + pathElements, + pathIndices, + root: tree.root() + } +} + +/** + * Generate SNARK proof for withdrawal + * @param deposit Deposit object + * @param recipient Funds recipient + * @param relayer Relayer address + * @param fee Relayer fee + * @param refund Receive ether for exchanged tokens + */ +async function generateProof({ deposit, recipient, events, relayerAddress = 0, fee = 0, refund = 0 }) { + // Compute merkle proof of our commitment + const { root, pathElements, pathIndices } = generateMerkleProof({ deposit, events }) + + // Prepare circuit input + const input = { + // Public snark inputs + root: root, + nullifierHash: deposit.nullifierHash, + recipient: bigInt(recipient), + relayer: bigInt(relayerAddress), + fee: bigInt(fee), + refund: bigInt(refund), + + // Private snark inputs + nullifier: deposit.nullifier, + secret: deposit.secret, + pathElements, + pathIndices + } + + console.log('Generating SNARK proof') + console.time('Proof time') + const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) + const { proof } = websnarkUtils.toSolidityInput(proofData) + console.timeEnd('Proof time') + + const args = [ + toHex(input.root), + toHex(input.nullifierHash), + toHex(input.recipient, 20), + toHex(input.relayer, 20), + toHex(input.fee), + toHex(input.refund) + ] + + return { proof, args } +} + +async function initialize({ merkleTreeHeight }) { + MERKLE_TREE_HEIGHT = merkleTreeHeight + groth16 = await buildGroth16() +} + +module.exports = { + initialize, + createDeposit, + generateProof, + generateMerkleProof, + rbigint, + bigInt, + toHex, + pedersenHash +} diff --git a/package.json b/package.json index 7627183..d51c091 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,14 @@ { - "name": "cli-tornado", + "name": "tornado-cli", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "core.js", + "files": [ + "config.js", + "core.js", + "build", + "lib" + ], "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, @@ -17,7 +23,8 @@ "gas-price-oracle": "^0.2.2", "snarkjs": "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5", "web3": "^1.2.8", - "websnark": "git+https://github.com/tornadocash/websnark.git#4c0af6a8b65aabea3c09f377f63c44e7a58afa6d" + "websnark": "git+https://github.com/tornadocash/websnark.git#4c0af6a8b65aabea3c09f377f63c44e7a58afa6d", + "fixed-merkle-tree": "^0.5.0" }, "devDependencies": { "eslint": "^7.0.0" diff --git a/yarn.lock b/yarn.lock index d5d3093..2e5928e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -758,6 +758,16 @@ circom@0.0.35: typedarray-to-buffer "^3.1.5" web3 "^1.2.11" +"circomlib@git+https://github.com/tornadocash/circomlib.git#5beb6aee94923052faeecea40135d45b6ce6172c": + version "0.0.20" + resolved "git+https://github.com/tornadocash/circomlib.git#5beb6aee94923052faeecea40135d45b6ce6172c" + dependencies: + blake-hash "^1.1.0" + blake2b "^2.1.3" + snarkjs "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5" + typedarray-to-buffer "^3.1.5" + web3 "^1.2.11" + class-is@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" @@ -1638,6 +1648,14 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +fixed-merkle-tree@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/fixed-merkle-tree/-/fixed-merkle-tree-0.5.0.tgz#401cdcf3d670c1e18bc7d3a8e81322eb1b27c1d1" + integrity sha512-egOy12EzVATX3Ru2/SLtnWprVpy/sbPCt/MbeG3ANB28jykWLEYj7EjinFnOxtsgR3gTHU6xYXX53yMn/bZqyw== + dependencies: + circomlib "git+https://github.com/tornadocash/circomlib.git#5beb6aee94923052faeecea40135d45b6ce6172c" + snarkjs "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5" + flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0"