tornado-nova/src/index.js

197 lines
5.7 KiB
JavaScript
Raw Normal View History

2020-04-09 22:08:16 +02:00
/* eslint-disable no-console */
2021-06-06 19:31:32 +02:00
const MerkleTree = require('fixed-merkle-tree')
const { ethers } = require('hardhat')
2021-06-15 22:02:59 +02:00
const { toFixedHex, poseidonHash2, getExtDataHash, FIELD_SIZE, packEncryptedMessage } = require('./utils')
const Utxo = require('./utxo')
2020-04-09 12:36:45 +02:00
2021-06-06 19:31:32 +02:00
const { prove } = require('./prover')
2020-04-09 20:38:10 +02:00
const MERKLE_TREE_HEIGHT = 5
2020-04-09 12:36:45 +02:00
2021-06-09 12:56:33 +02:00
async function buildMerkleTree({ tornadoPool }) {
2020-04-09 21:12:45 +02:00
console.log('Getting contract state...')
2021-06-09 12:56:33 +02:00
const filter = tornadoPool.filters.NewCommitment()
const events = await tornadoPool.queryFilter(filter, 0)
2020-04-09 21:12:45 +02:00
const leaves = events
2021-06-09 12:56:33 +02:00
.sort((a, b) => a.args.index - b.args.index) // todo sort by event date
.map((e) => toFixedHex(e.args.commitment))
// console.log('leaves', leaves)
2021-06-06 19:31:32 +02:00
return new MerkleTree(MERKLE_TREE_HEIGHT, leaves, { hashFunction: poseidonHash2 })
2020-04-09 12:36:45 +02:00
}
async function getProof({ inputs, outputs, tree, extAmount, fee, recipient, relayer }) {
// todo shuffle inputs and outputs
2021-06-07 12:12:15 +02:00
let inputMerklePathIndices = []
let inputMerklePathElements = []
for (const input of inputs) {
if (input.amount > 0) {
const index = tree.indexOf(toFixedHex(input.getCommitment()))
if (index < 0) {
2021-06-15 22:02:59 +02:00
throw new Error(`Input commitment ${toFixedHex(input.getCommitment())} was not found`)
}
inputMerklePathIndices.push(index)
inputMerklePathElements.push(tree.path(index).pathElements)
} else {
inputMerklePathIndices.push(0)
inputMerklePathElements.push(new Array(tree.levels).fill(0))
}
2021-06-07 12:12:15 +02:00
}
2020-04-09 21:12:45 +02:00
const oldRoot = tree.root()
for (const output of outputs) {
output.index = tree.elements().length
tree.insert(output.getCommitment())
}
const outputIndex = tree.elements().length - 1
2021-06-09 12:58:57 +02:00
const outputPath = tree.path(outputIndex).pathElements
2021-06-15 14:48:26 +02:00
//encrypt(encryptedPublicKey, { data }, 'x25519-xsalsa20-poly1305')
const extData = {
recipient: toFixedHex(recipient, 20),
relayer: toFixedHex(relayer, 20),
2021-06-15 22:02:59 +02:00
encryptedOutput1: packEncryptedMessage(
outputs[0].keypair.encrypt({ blinding: outputs[0].blinding, amount: outputs[0].amount }),
),
encryptedOutput2: packEncryptedMessage(
outputs[1].keypair.encrypt({ blinding: outputs[1].blinding, amount: outputs[1].amount }),
),
}
2021-06-07 12:12:15 +02:00
const extDataHash = getExtDataHash(extData)
2020-04-09 21:12:45 +02:00
let input = {
root: oldRoot,
2021-06-07 12:12:15 +02:00
newRoot: tree.root(),
2021-06-09 12:56:33 +02:00
inputNullifier: inputs.map((x) => x.getNullifier()),
outputCommitment: outputs.map((x) => x.getCommitment()),
2021-06-07 12:12:15 +02:00
extAmount,
fee,
extDataHash,
2020-04-09 21:12:45 +02:00
// data for 2 transaction inputs
2021-06-09 12:56:33 +02:00
inAmount: inputs.map((x) => x.amount),
2021-06-09 13:19:22 +02:00
inPrivateKey: inputs.map((x) => x.keypair.privkey),
2021-06-09 12:56:33 +02:00
inBlinding: inputs.map((x) => x.blinding),
inPathIndices: inputMerklePathIndices,
inPathElements: inputMerklePathElements,
2020-04-09 21:12:45 +02:00
// data for 2 transaction outputs
2021-06-09 12:56:33 +02:00
outAmount: outputs.map((x) => x.amount),
outBlinding: outputs.map((x) => x.blinding),
2021-06-09 13:19:22 +02:00
outPubkey: outputs.map((x) => x.keypair.pubkey),
2021-06-09 12:58:57 +02:00
outPathIndices: outputIndex >> Math.log2(outputs.length),
outPathElements: outputPath.slice(Math.log2(outputs.length)),
2020-04-09 21:12:45 +02:00
}
//console.log('SNARK input', input)
2020-04-09 21:12:45 +02:00
console.log('Generating SNARK proof...')
2021-06-09 12:58:57 +02:00
const proof = await prove(input, `./artifacts/circuits/transaction${inputs.length}`)
2020-04-09 21:12:45 +02:00
const args = [
2021-06-06 19:31:32 +02:00
toFixedHex(input.root),
toFixedHex(input.newRoot),
2021-06-09 12:56:33 +02:00
inputs.map((x) => toFixedHex(x.getNullifier())),
outputs.map((x) => toFixedHex(x.getCommitment())),
toFixedHex(extAmount),
toFixedHex(fee),
extData,
toFixedHex(extDataHash),
2020-04-09 21:12:45 +02:00
]
// console.log('Solidity args', args)
2020-04-09 21:12:45 +02:00
2021-06-07 12:12:15 +02:00
return {
proof,
args,
}
}
2021-06-15 22:02:59 +02:00
async function deposit({ tornadoPool, utxo }) {
const inputs = [new Utxo(), new Utxo()]
2021-06-15 22:02:59 +02:00
const outputs = [utxo, new Utxo()]
2021-06-07 12:12:15 +02:00
const { proof, args } = await getProof({
inputs,
outputs,
2021-06-09 12:56:33 +02:00
tree: await buildMerkleTree({ tornadoPool }),
2021-06-15 22:02:59 +02:00
extAmount: utxo.amount,
2021-06-07 12:12:15 +02:00
fee: 0,
recipient: 0,
relayer: 0,
})
2020-04-09 21:12:45 +02:00
console.log('Sending deposit transaction...')
2021-06-09 12:56:33 +02:00
const receipt = await tornadoPool.transaction(proof, ...args, {
2021-06-15 22:02:59 +02:00
value: utxo.amount,
2021-06-09 12:56:33 +02:00
gasLimit: 1e6,
})
console.log(`Receipt ${receipt.hash}`)
return outputs[0]
2020-04-09 21:12:45 +02:00
}
2021-06-15 13:47:54 +02:00
async function merge({ tornadoPool }) {
const amount = 1e6
2021-06-15 22:02:59 +02:00
const inputs = new Array(16).fill(0).map((_) => new Utxo())
2021-06-15 13:47:54 +02:00
const outputs = [new Utxo({ amount }), new Utxo()]
const { proof, args } = await getProof({
inputs,
outputs,
tree: await buildMerkleTree({ tornadoPool }),
extAmount: amount,
fee: 0,
recipient: 0,
relayer: 0,
})
const receipt = await tornadoPool.transaction(proof, ...args, {
value: amount,
gasLimit: 1e6,
})
console.log(`Receipt ${receipt.hash}`)
return outputs[0]
}
2021-06-15 22:02:59 +02:00
async function transact({ tornadoPool, input, output }) {
const inputs = [input, new Utxo()]
const outputs = [output, new Utxo()]
2020-04-09 22:08:16 +02:00
const { proof, args } = await getProof({
inputs,
outputs,
2021-06-09 12:56:33 +02:00
tree: await buildMerkleTree({ tornadoPool }),
2020-04-09 22:08:16 +02:00
extAmount: 0,
fee: 0,
recipient: 0,
relayer: 0,
})
2020-04-09 21:12:45 +02:00
2020-04-09 22:08:16 +02:00
console.log('Sending transfer transaction...')
2021-06-09 12:56:33 +02:00
const receipt = await tornadoPool.transaction(proof, ...args, { gasLimit: 1e6 })
console.log(`Receipt ${receipt.hash}`)
return outputs[0]
2020-04-09 20:58:01 +02:00
}
2021-06-15 22:02:59 +02:00
async function withdraw({ tornadoPool, input, change, recipient }) {
const inputs = [input, new Utxo()]
const outputs = [change, new Utxo()]
2020-04-09 22:36:41 +02:00
const { proof, args } = await getProof({
inputs,
outputs,
2021-06-09 12:56:33 +02:00
tree: await buildMerkleTree({ tornadoPool }),
2021-06-15 22:02:59 +02:00
extAmount: FIELD_SIZE.sub(input.amount.sub(change.amount)),
2020-04-09 22:36:41 +02:00
fee: 0,
2021-06-09 12:56:33 +02:00
recipient,
2020-04-09 22:36:41 +02:00
relayer: 0,
})
2020-04-09 22:36:41 +02:00
console.log('Sending withdraw transaction...')
2021-06-09 12:56:33 +02:00
const receipt = await tornadoPool.transaction(proof, ...args, { gasLimit: 1e6 })
console.log(`Receipt ${receipt.hash}`)
2020-04-09 12:36:45 +02:00
}
2021-06-15 13:47:54 +02:00
module.exports = { deposit, withdraw, transact, merge }