tornado-trees/test/tornadoTrees.test.js

293 lines
11 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-02 14:20:59 +03:00
const controller = require('../src/controller')
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-01 16:40:32 +03:00
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-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),
}))
2021-02-05 23:28:51 +03:00
;({ input, args } = controller.batchTreeUpdate(tree, registeredEvents.slice(0, 4)))
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 () => {
for (let i = 4; i < 6; i++) {
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,
)
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 6 new deposits for the new trees
for (let i = 0; i < notes.length; i++) {
await register(notes[i], newTornadoTrees, tornadoProxy)
}
// get 2 events from v1 tress
let events = notes.slice(4).map((note) => ({
hash: toFixedHex(note.commitment),
instance: toFixedHex(note.instance, 20),
block: toFixedHex(note.depositBlock, 4),
}))
const registeredEvents = await newTornadoTrees.queryFilter(depositDataEventFilter)
events = events.concat(
registeredEvents.slice(0, 2).map((e) => ({
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(6).map((e) => ({
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
})