tornado-trees/test/tornadoTrees.test.js

310 lines
12 KiB
JavaScript
Raw Normal View History

2021-02-02 14:38:11 +03:00
/* global ethers */
2021-02-02 14:27:58 +03:00
const { expect } = require('chai')
2021-02-01 16:40:32 +03:00
const { toFixedHex, poseidonHash2, randomBN } = require('../src/utils')
const MerkleTree = require('fixed-merkle-tree')
2021-02-07 13:27:00 +03:00
const controller = require('../src/index')
2021-02-01 16:40:32 +03:00
async function register(note, tornadoTrees, from) {
2021-02-02 14:27:58 +03:00
await tornadoTrees
.connect(from)
.register(
note.instance,
toFixedHex(note.commitment),
toFixedHex(note.nullifierHash),
note.depositBlock,
note.withdrawalBlock,
)
2021-02-01 16:40:32 +03:00
}
const levels = 20
const CHUNK_TREE_HEIGHT = 2
2021-02-02 14:20:59 +03:00
const instances = [
'0x1111000000000000000000000000000000001111',
'0x2222000000000000000000000000000000002222',
'0x3333000000000000000000000000000000003333',
'0x4444000000000000000000000000000000004444',
]
2021-02-01 16:40:32 +03:00
2021-02-02 14:20:59 +03:00
const blocks = ['0xaaaaaaaa', '0xbbbbbbbb', '0xcccccccc', '0xdddddddd']
2021-02-01 16:40:32 +03:00
2021-02-02 14:27:58 +03:00
describe('TornadoTrees', function () {
2021-02-02 14:20:59 +03:00
let tree
let operator
let tornadoProxy
let verifier
let tornadoTrees
2021-02-04 17:17:21 +03:00
let tornadoTreesV1
2021-02-02 14:20:59 +03:00
let notes
2021-02-05 23:28:51 +03:00
let depositDataEventFilter
2021-02-02 22:46:51 +03:00
const depositEvents = []
const withdrawalEvents = []
2021-02-02 14:20:59 +03:00
2021-02-02 14:27:58 +03:00
beforeEach(async function () {
2021-02-02 14:20:59 +03:00
tree = new MerkleTree(levels, [], { hashFunction: poseidonHash2 })
;[operator, tornadoProxy] = await ethers.getSigners()
2021-02-02 14:27:58 +03:00
const BatchTreeUpdateVerifier = await ethers.getContractFactory('BatchTreeUpdateVerifier')
2021-02-02 14:20:59 +03:00
verifier = await BatchTreeUpdateVerifier.deploy()
2021-02-04 17:17:21 +03:00
const TornadoTreesV1 = await ethers.getContractFactory('TornadoTreesV1Mock')
tornadoTreesV1 = await TornadoTreesV1.deploy(0, 0, tree.root(), tree.root())
2021-02-02 14:20:59 +03:00
notes = []
2021-02-01 16:40:32 +03:00
for (let i = 0; i < 2 ** CHUNK_TREE_HEIGHT; i++) {
notes[i] = {
instance: instances[i % instances.length],
depositBlock: blocks[i % blocks.length],
withdrawalBlock: 2 + i + i * 4 * 60 * 24,
commitment: randomBN(),
nullifierHash: randomBN(),
}
2021-02-04 17:17:21 +03:00
await register(notes[i], tornadoTreesV1, tornadoProxy)
2021-02-02 22:46:51 +03:00
depositEvents[i] = {
hash: toFixedHex(notes[i].commitment),
instance: toFixedHex(notes[i].instance, 20),
block: toFixedHex(notes[i].depositBlock, 4),
}
withdrawalEvents[i] = {
hash: toFixedHex(notes[i].nullifierHash),
instance: toFixedHex(notes[i].instance, 20),
block: toFixedHex(notes[i].withdrawalBlock, 4),
}
2021-02-01 16:40:32 +03:00
}
2021-02-04 17:17:21 +03:00
const TornadoTrees = await ethers.getContractFactory('TornadoTreesMock')
tornadoTrees = await TornadoTrees.deploy(
operator.address,
tornadoProxy.address,
tornadoTreesV1.address,
verifier.address,
2021-02-06 16:13:22 +03:00
{
2021-02-17 18:10:25 +03:00
depositsFrom: 1,
depositsStep: 1,
withdrawalsFrom: 2,
withdrawalsStep: 2,
2021-02-06 16:13:22 +03:00
},
2021-02-04 17:17:21 +03:00
)
2021-02-05 23:28:51 +03:00
depositDataEventFilter = tornadoTrees.filters.DepositData()
2021-02-01 16:40:32 +03:00
})
2021-02-02 22:46:51 +03:00
describe('#updateDepositTree', () => {
it('should check hash', async () => {
const { args } = controller.batchTreeUpdate(tree, depositEvents)
const solHash = await tornadoTrees.updateDepositTreeMock(...args.slice(1))
expect(solHash).to.be.equal(args[0])
})
2021-02-04 20:14:06 +03:00
it('should prove snark', async () => {
2021-02-02 22:46:51 +03:00
const { input, args } = controller.batchTreeUpdate(tree, depositEvents)
const proof = await controller.prove(input, './artifacts/circuits/BatchTreeUpdate')
await tornadoTrees.updateDepositTree(proof, ...args)
const updatedRoot = await tornadoTrees.depositRoot()
expect(updatedRoot).to.be.equal(tree.root())
})
it('should work for non-empty tree', async () => {
let { input, args } = controller.batchTreeUpdate(tree, depositEvents)
let proof = await controller.prove(input, './artifacts/circuits/BatchTreeUpdate')
await tornadoTrees.updateDepositTree(proof, ...args)
let updatedRoot = await tornadoTrees.depositRoot()
expect(updatedRoot).to.be.equal(tree.root())
//
for (let i = 0; i < notes.length; i++) {
await register(notes[i], tornadoTrees, tornadoProxy)
}
;({ input, args } = controller.batchTreeUpdate(tree, depositEvents))
proof = await controller.prove(input, './artifacts/circuits/BatchTreeUpdate')
await tornadoTrees.updateDepositTree(proof, ...args)
updatedRoot = await tornadoTrees.depositRoot()
expect(updatedRoot).to.be.equal(tree.root())
})
2021-02-05 20:38:52 +03:00
it('should work with events from contracts', async () => {
let { input, args } = controller.batchTreeUpdate(tree, depositEvents)
let proof = await controller.prove(input, './artifacts/circuits/BatchTreeUpdate')
await tornadoTrees.updateDepositTree(proof, ...args)
let updatedRoot = await tornadoTrees.depositRoot()
expect(updatedRoot).to.be.equal(tree.root())
2021-02-05 23:28:51 +03:00
const migratedEvents = await tornadoTrees.queryFilter(depositDataEventFilter)
2021-02-05 20:41:20 +03:00
migratedEvents.forEach((e, i) => {
expect(e.args.index).to.be.equal(i)
})
2021-02-05 20:38:52 +03:00
//
for (let i = 0; i < notes.length; i++) {
await register(notes[i], tornadoTrees, tornadoProxy)
}
2021-02-05 23:28:51 +03:00
let registeredEvents = await tornadoTrees.queryFilter(depositDataEventFilter)
2021-02-05 20:38:52 +03:00
registeredEvents = registeredEvents.map((e) => ({
hash: toFixedHex(e.args.hash),
instance: toFixedHex(e.args.instance, 20),
block: toFixedHex(e.args.block, 4),
}))
;({ input, args } = controller.batchTreeUpdate(tree, registeredEvents.slice(0, notes.length)))
2021-02-05 20:38:52 +03:00
proof = await controller.prove(input, './artifacts/circuits/BatchTreeUpdate')
await tornadoTrees.updateDepositTree(proof, ...args)
updatedRoot = await tornadoTrees.depositRoot()
expect(updatedRoot).to.be.equal(tree.root())
})
2021-02-06 00:04:15 +03:00
it('should work for batch+N filled v1 tree', async () => {
const batchSize = 2 ** CHUNK_TREE_HEIGHT
for (let i = batchSize; i < batchSize + 2; i++) {
2021-02-06 00:04:15 +03:00
notes.push({
instance: instances[i % instances.length],
depositBlock: blocks[i % blocks.length],
withdrawalBlock: 2 + i + i * 4 * 60 * 24,
commitment: randomBN(),
nullifierHash: randomBN(),
})
await register(notes[i], tornadoTreesV1, tornadoProxy)
}
const TornadoTrees = await ethers.getContractFactory('TornadoTreesMock')
const newTornadoTrees = await TornadoTrees.deploy(
operator.address,
tornadoProxy.address,
tornadoTreesV1.address,
verifier.address,
2021-02-06 16:13:22 +03:00
{
2021-02-17 18:10:25 +03:00
depositsFrom: 1,
depositsStep: 1,
withdrawalsFrom: 2,
withdrawalsStep: 2,
2021-02-06 16:13:22 +03:00
},
2021-02-06 00:04:15 +03:00
)
// load first batchSize deposits
2021-02-06 00:04:15 +03:00
let { input, args } = controller.batchTreeUpdate(tree, depositEvents)
let proof = await controller.prove(input, './artifacts/circuits/BatchTreeUpdate')
await newTornadoTrees.updateDepositTree(proof, ...args)
let updatedRoot = await newTornadoTrees.depositRoot()
expect(updatedRoot).to.be.equal(tree.root())
// register 2 * `notes.length` new deposits on the new trees
for (let i = 0; i < notes.length; i++) {
await register(notes[i], newTornadoTrees, tornadoProxy)
}
2021-02-06 00:04:15 +03:00
for (let i = 0; i < notes.length; i++) {
await register(notes[i], newTornadoTrees, tornadoProxy)
}
// get 2 extra events from v1 tress
let events = notes.slice(batchSize).map((note) => ({
2021-02-06 00:04:15 +03:00
hash: toFixedHex(note.commitment),
instance: toFixedHex(note.instance, 20),
block: toFixedHex(note.depositBlock, 4),
}))
let registeredEvents = await newTornadoTrees.queryFilter(depositDataEventFilter)
registeredEvents = registeredEvents.slice(batchSize) // cut processed deposits from v1
2021-02-06 00:04:15 +03:00
events = events.concat(
registeredEvents.slice(0, batchSize - 2).map((e) => ({
2021-02-06 00:04:15 +03:00
hash: toFixedHex(e.args.hash),
instance: toFixedHex(e.args.instance, 20),
block: toFixedHex(e.args.block, 4),
})),
)
//
;({ input, args } = controller.batchTreeUpdate(tree, events))
proof = await controller.prove(input, './artifacts/circuits/BatchTreeUpdate')
await newTornadoTrees.updateDepositTree(proof, ...args)
updatedRoot = await newTornadoTrees.depositRoot()
expect(updatedRoot).to.be.equal(tree.root())
events = registeredEvents.slice(batchSize - 2, 2 * batchSize - 2).map((e) => ({
2021-02-06 00:04:15 +03:00
hash: toFixedHex(e.args.hash),
instance: toFixedHex(e.args.instance, 20),
block: toFixedHex(e.args.block, 4),
}))
;({ input, args } = controller.batchTreeUpdate(tree, events))
proof = await controller.prove(input, './artifacts/circuits/BatchTreeUpdate')
await newTornadoTrees.updateDepositTree(proof, ...args)
updatedRoot = await newTornadoTrees.depositRoot()
expect(updatedRoot).to.be.equal(tree.root())
})
2021-02-02 22:46:51 +03:00
it('should reject for partially filled tree')
it('should reject for outdated deposit root')
it('should reject for incorrect insert index')
it('should reject for overflows of newRoot')
it('should reject for invalid sha256 args')
2021-02-01 16:40:32 +03:00
})
2021-02-02 22:46:51 +03:00
describe('#getRegisteredDeposits', () => {
it('should work', async () => {
2021-02-05 20:19:36 +03:00
for (let i = 0; i < 2 ** CHUNK_TREE_HEIGHT; i++) {
notes[i] = {
instance: instances[i % instances.length],
depositBlock: blocks[i % blocks.length],
withdrawalBlock: 2 + i + i * 4 * 60 * 24,
commitment: randomBN(),
nullifierHash: randomBN(),
}
await register(notes[i], tornadoTrees, tornadoProxy)
}
2021-02-02 22:46:51 +03:00
const abi = new ethers.utils.AbiCoder()
2021-02-04 20:47:47 +03:00
const count = await tornadoTrees.depositsLength()
const _deposits = await tornadoTrees.getRegisteredDeposits()
2021-02-05 20:19:36 +03:00
expect(count).to.be.equal(notes.length * 2)
2021-02-02 22:46:51 +03:00
_deposits.forEach((hash, i) => {
2021-02-05 20:19:36 +03:00
if (i < notes.length) {
expect(hash).to.be.equal('0x0000000000000000000000000000000000000000000000000000000000000000')
} else {
const index = i - notes.length
const encodedData = abi.encode(
['address', 'bytes32', 'uint256'],
[notes[index].instance, toFixedHex(notes[index].commitment), notes[index].depositBlock],
)
const leaf = ethers.utils.keccak256(encodedData)
expect(leaf).to.be.equal(hash)
}
2021-02-02 22:46:51 +03:00
})
// res.length.should.be.equal(1)
// res[0].should.be.true
// await tornadoTrees.updateRoots([note1DepositLeaf], [])
// res = await tornadoTrees.getRegisteredDeposits()
// res.length.should.be.equal(0)
// await registerDeposit(note2, tornadoTrees)
// res = await tornadoTrees.getRegisteredDeposits()
// // res[0].should.be.true
})
2021-02-01 16:40:32 +03:00
})
2021-02-02 14:20:59 +03:00
2021-02-02 22:46:51 +03:00
describe('#getRegisteredWithdrawals', () => {
it('should work', async () => {
2021-02-05 20:19:36 +03:00
for (let i = 0; i < 2 ** CHUNK_TREE_HEIGHT; i++) {
notes[i] = {
instance: instances[i % instances.length],
depositBlock: blocks[i % blocks.length],
withdrawalBlock: 2 + i + i * 4 * 60 * 24,
commitment: randomBN(),
nullifierHash: randomBN(),
}
await register(notes[i], tornadoTrees, tornadoProxy)
}
2021-02-02 22:46:51 +03:00
const abi = new ethers.utils.AbiCoder()
2021-02-04 20:47:47 +03:00
const count = await tornadoTrees.withdrawalsLength()
const _withdrawals = await tornadoTrees.getRegisteredWithdrawals()
2021-02-05 20:19:36 +03:00
expect(count).to.be.equal(notes.length * 2)
2021-02-02 22:46:51 +03:00
_withdrawals.forEach((hash, i) => {
2021-02-05 20:19:36 +03:00
if (i < notes.length) {
expect(hash).to.be.equal('0x0000000000000000000000000000000000000000000000000000000000000000')
} else {
const index = i - notes.length
const encodedData = abi.encode(
['address', 'bytes32', 'uint256'],
[notes[index].instance, toFixedHex(notes[index].nullifierHash), notes[index].withdrawalBlock],
)
const leaf = ethers.utils.keccak256(encodedData)
expect(leaf).to.be.equal(hash)
}
2021-02-02 22:46:51 +03:00
})
})
})
2021-02-01 16:40:32 +03:00
})