tornado-relayer/src/treeWatcher.js

137 lines
4.3 KiB
JavaScript
Raw Normal View History

2020-09-28 04:28:34 +02:00
const MerkleTree = require('fixed-merkle-tree')
const { minerMerkleTreeHeight, torn, netId } = require('./config')
2022-04-08 12:29:37 +02:00
const { poseidonHash2, toBN, logRelayerError } = require('./utils')
const resolver = require('./modules/resolver')
const web3 = require('./modules/web3')('ws')
2020-11-04 20:23:14 +01:00
const MinerABI = require('../abis/mining.abi.json')
const { redis } = require('./modules/redis')
2020-11-04 20:23:14 +01:00
let contract
2020-09-28 04:28:34 +02:00
2021-03-31 10:39:54 +02:00
// eslint-disable-next-line no-unused-vars
2020-09-28 04:28:34 +02:00
let tree, eventSubscription, blockSubscription
2021-10-08 18:50:21 +02:00
async function fetchEvents(fromBlock, toBlock) {
if (fromBlock <= toBlock) {
try {
return await contract.getPastEvents('NewAccount', {
fromBlock,
toBlock,
})
} catch (error) {
const midBlock = (fromBlock + toBlock) >> 1
if (midBlock - fromBlock < 2) {
throw new Error(`error fetching events: ${error.message}`)
}
const arr1 = await fetchEvents(fromBlock, midBlock)
const arr2 = await fetchEvents(midBlock + 1, toBlock)
return [...arr1, ...arr2]
}
2021-03-02 05:38:16 +01:00
}
2021-10-08 18:50:21 +02:00
return []
2020-09-28 04:28:34 +02:00
}
async function processNewEvent(err, event) {
if (err) {
throw new Error(`Event handler error: ${err}`)
// console.error(err)
// return
}
2020-10-14 13:43:38 +02:00
console.log(
`New account event
Index: ${event.returnValues.index}
Commitment: ${event.returnValues.commitment}
Nullifier: ${event.returnValues.nullifier}
EncAcc: ${event.returnValues.encryptedAccount}`,
)
2020-09-28 04:28:34 +02:00
const { commitment, index } = event.returnValues
if (tree.elements().length === Number(index)) {
tree.insert(toBN(commitment))
await updateRedis()
} else if (tree.elements().length === Number(index) + 1) {
console.log('Replacing element', index)
tree.update(index, toBN(commitment))
await updateRedis()
} else {
console.log(`Invalid element index ${index}, rebuilding tree`)
2021-03-31 10:35:43 +02:00
rebuild()
2020-09-28 04:28:34 +02:00
}
}
async function processNewBlock(err) {
if (err) {
throw new Error(`Event handler error: ${err}`)
// console.error(err)
// return
}
2020-09-28 16:54:54 +02:00
// what if updateRedis takes more than 15 sec?
2020-09-28 04:28:34 +02:00
await updateRedis()
}
async function updateRedis() {
const rootOnContract = await contract.methods.getLastAccountRoot().call()
if (!tree.root().eq(toBN(rootOnContract))) {
console.log(`Invalid tree root: ${tree.root()} != ${toBN(rootOnContract)}, rebuilding tree`)
2021-03-31 10:35:43 +02:00
rebuild()
2020-09-28 04:28:34 +02:00
return
}
const rootInRedis = await redis.get('tree:root')
if (!rootInRedis || !tree.root().eq(toBN(rootInRedis))) {
const serializedTree = JSON.stringify(tree.serialize())
await redis.set('tree:elements', serializedTree)
await redis.set('tree:root', tree.root().toString())
await redis.publish('treeUpdate', tree.root().toString())
console.log('Updated tree in redis, new root:', tree.root().toString())
} else {
console.log('Tree in redis is up to date, skipping update')
}
}
2021-03-31 10:35:43 +02:00
function rebuild() {
process.exit(1)
// await eventSubscription.unsubscribe()
// await blockSubscription.unsubscribe()
// setTimeout(init, 3000)
2020-09-28 04:28:34 +02:00
}
async function init() {
2021-03-02 05:38:16 +01:00
try {
console.log('Initializing')
const miner = await resolver.resolve(torn.miningV2.address)
contract = new web3.eth.Contract(MinerABI, miner)
2021-10-08 18:50:21 +02:00
const cachedEvents = require(`../cache/accounts_farmer_${netId}.json`)
const cachedCommitments = cachedEvents.map(e => toBN(e.commitment))
const toBlock = await web3.eth.getBlockNumber()
const [{ blockNumber: fromBlock }] = cachedEvents.slice(-1)
const newEvents = await fetchEvents(fromBlock + 1, toBlock)
const newCommitments = newEvents
.sort((a, b) => a.returnValues.index - b.returnValues.index)
.map(e => toBN(e.returnValues.commitment))
.filter((item, index, arr) => !index || item !== arr[index - 1])
2021-10-08 18:50:21 +02:00
const commitments = cachedCommitments.concat(newCommitments)
tree = new MerkleTree(minerMerkleTreeHeight, commitments, { hashFunction: poseidonHash2 })
2021-03-31 10:35:43 +02:00
await updateRedis()
2021-10-08 18:50:21 +02:00
console.log(`Rebuilt tree with ${commitments.length} elements, root: ${tree.root()}`)
eventSubscription = contract.events.NewAccount({ fromBlock: toBlock + 1 }, processNewEvent)
2021-03-02 05:38:16 +01:00
blockSubscription = web3.eth.subscribe('newBlockHeaders', processNewBlock)
} catch (e) {
2022-04-20 07:41:59 +02:00
await logRelayerError(redis, e)
2021-03-02 05:38:16 +01:00
console.error('error on init treeWatcher', e.message)
}
2020-09-28 04:28:34 +02:00
}
init()
2020-10-02 12:26:05 +02:00
process.on('unhandledRejection', error => {
2020-09-28 04:28:34 +02:00
console.error('Unhandled promise rejection', error)
process.exit(1)
})