forked from tornadocash/nova-ui
285 lines
6.8 KiB
JavaScript
285 lines
6.8 KiB
JavaScript
import path from 'path'
|
|
import { stat, readFile, writeFile } from 'fs/promises'
|
|
import { Contract, providers, utils } from 'ethers'
|
|
|
|
import { BatchEventsService } from './services/batch'
|
|
import { getAllAccounts, getAllCommitments, getAllNullifiers } from './services/graph'
|
|
import { POOL_CONTRACT, BRIDGE_HELPER, RPC_LIST, ChainId, CHAINS, numbers } from './services/constants'
|
|
import { zipAsync, unzipAsync } from './services/zip'
|
|
import { poolAbi } from './services/pool'
|
|
import { bridgeAbi } from './services/bridgeHelper'
|
|
|
|
const { getAddress } = utils
|
|
const { StaticJsonRpcProvider } = providers
|
|
|
|
const EVENT_PATH = './static'
|
|
|
|
async function existsAsync(fileOrDir) {
|
|
try {
|
|
await stat(fileOrDir);
|
|
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const getProvider = (chainId) => {
|
|
return new StaticJsonRpcProvider({ skipFetchSetup: true, url: RPC_LIST[chainId] }, chainId)
|
|
}
|
|
|
|
const getTornadoPool = (chainId, provider) => {
|
|
const TornadoPool = new Contract(POOL_CONTRACT[chainId], poolAbi, provider)
|
|
|
|
return {
|
|
TornadoPool,
|
|
BatchEventsService: new BatchEventsService({
|
|
provider,
|
|
contract: TornadoPool
|
|
})
|
|
}
|
|
}
|
|
|
|
const getBridgeHelper = (chainId, provider) => {
|
|
const BridgeHelper = new Contract(BRIDGE_HELPER[chainId], bridgeAbi, provider)
|
|
|
|
return {
|
|
BridgeHelper,
|
|
BridgeEventsService: new BatchEventsService({
|
|
provider,
|
|
contract: BridgeHelper
|
|
})
|
|
}
|
|
}
|
|
|
|
const loadEvents = async (fileName, deployedBlock) => {
|
|
fileName = fileName.toLowerCase()
|
|
|
|
const filePath = path.join(EVENT_PATH, fileName + '.zip')
|
|
|
|
if (!(await existsAsync(filePath))) {
|
|
return {
|
|
events: [],
|
|
lastBlock: deployedBlock
|
|
}
|
|
}
|
|
|
|
try {
|
|
const data = await readFile(filePath)
|
|
const { [fileName]: content } = await unzipAsync(data)
|
|
|
|
const events = JSON.parse(new TextDecoder().decode(content))
|
|
|
|
const lastBlock = events && Array.isArray(events) && events[events.length - 1]
|
|
? events[events.length - 1].blockNumber
|
|
: deployedBlock
|
|
|
|
return {
|
|
events,
|
|
lastBlock
|
|
}
|
|
} catch {
|
|
return {
|
|
events: [],
|
|
lastBlock: deployedBlock
|
|
}
|
|
}
|
|
}
|
|
|
|
const saveEvents = async (fileName, events) => {
|
|
fileName = fileName.toLowerCase()
|
|
|
|
const filePath = path.join(EVENT_PATH, fileName + '.zip')
|
|
|
|
const payload = await zipAsync({
|
|
[fileName]: new TextEncoder().encode(JSON.stringify(events, null, 2) + '\n')
|
|
})
|
|
|
|
await writeFile(filePath, payload)
|
|
}
|
|
|
|
const syncAccounts = async (chainId, BatchEventsService) => {
|
|
const fileName = `accounts_${chainId}.json`
|
|
|
|
console.log(`Syncing ${fileName}`)
|
|
|
|
const cachedEvents = await loadEvents(fileName, CHAINS[chainId].deployBlock)
|
|
|
|
const events = [...cachedEvents.events]
|
|
let fromBlock = cachedEvents.lastBlock + numbers.ONE
|
|
|
|
console.log({
|
|
cachedEvents: events.length,
|
|
cachedBlock: fromBlock
|
|
})
|
|
|
|
const { events: graphEvents, lastSyncBlock } = await getAllAccounts({
|
|
fromBlock,
|
|
chainId
|
|
})
|
|
|
|
console.log({
|
|
graphEvents: graphEvents.length,
|
|
graphBlock: lastSyncBlock
|
|
})
|
|
|
|
if (lastSyncBlock) {
|
|
events.push(...graphEvents)
|
|
fromBlock = lastSyncBlock
|
|
}
|
|
|
|
let nodeEvents = await BatchEventsService.getBatchEvents({
|
|
fromBlock,
|
|
type: 'PublicKey'
|
|
})
|
|
|
|
console.log({
|
|
nodeEvents: nodeEvents.length,
|
|
nodeBlock: nodeEvents && nodeEvents[nodeEvents.length - 1] ? nodeEvents[nodeEvents.length - 1].blockNumber : undefined
|
|
})
|
|
|
|
if (nodeEvents && nodeEvents.length) {
|
|
nodeEvents = nodeEvents.map(({ blockNumber, args }) => ({
|
|
key: args.key,
|
|
owner: getAddress(args.owner),
|
|
blockNumber,
|
|
}))
|
|
|
|
events.push(...nodeEvents)
|
|
}
|
|
|
|
await saveEvents(fileName, events)
|
|
}
|
|
|
|
const syncCommitments = async (chainId, BatchEventsService) => {
|
|
const fileName = `commitments_${chainId}.json`
|
|
|
|
console.log(`Syncing ${fileName}`)
|
|
|
|
const cachedEvents = await loadEvents(fileName, CHAINS[chainId].deployBlock)
|
|
|
|
const events = [...cachedEvents.events]
|
|
let fromBlock = cachedEvents.lastBlock + numbers.ONE
|
|
|
|
console.log({
|
|
cachedEvents: events.length,
|
|
cachedBlock: fromBlock
|
|
})
|
|
|
|
const { events: graphEvents, lastSyncBlock } = await getAllCommitments({
|
|
fromBlock,
|
|
chainId
|
|
})
|
|
|
|
console.log({
|
|
graphEvents: graphEvents.length,
|
|
graphBlock: lastSyncBlock
|
|
})
|
|
|
|
if (lastSyncBlock) {
|
|
events.push(...graphEvents)
|
|
fromBlock = lastSyncBlock
|
|
}
|
|
|
|
let nodeEvents = await BatchEventsService.getBatchEvents({
|
|
fromBlock,
|
|
type: 'NewCommitment'
|
|
})
|
|
|
|
console.log({
|
|
nodeEvents: nodeEvents.length,
|
|
nodeBlock: nodeEvents && nodeEvents[nodeEvents.length - 1] ? nodeEvents[nodeEvents.length - 1].blockNumber : undefined
|
|
})
|
|
|
|
if (nodeEvents && nodeEvents.length) {
|
|
nodeEvents = nodeEvents.map(({ blockNumber, transactionHash, args }) => ({
|
|
blockNumber,
|
|
transactionHash,
|
|
index: Number(args.index),
|
|
commitment: args.commitment,
|
|
encryptedOutput: args.encryptedOutput,
|
|
}))
|
|
|
|
events.push(...nodeEvents)
|
|
}
|
|
|
|
await saveEvents(fileName, events)
|
|
}
|
|
|
|
const syncNullifiers = async (chainId, BatchEventsService) => {
|
|
const fileName = `nullifiers_${chainId}.json`
|
|
|
|
console.log(`Syncing ${fileName}`)
|
|
|
|
const cachedEvents = await loadEvents(fileName, CHAINS[chainId].deployBlock)
|
|
|
|
const events = [...cachedEvents.events]
|
|
let fromBlock = cachedEvents.lastBlock + numbers.ONE
|
|
|
|
console.log({
|
|
cachedEvents: events.length,
|
|
cachedBlock: fromBlock
|
|
})
|
|
|
|
const { events: graphEvents, lastSyncBlock } = await getAllNullifiers({
|
|
fromBlock,
|
|
chainId
|
|
})
|
|
|
|
console.log({
|
|
graphEvents: graphEvents.length,
|
|
graphBlock: lastSyncBlock
|
|
})
|
|
|
|
if (lastSyncBlock) {
|
|
events.push(...graphEvents)
|
|
fromBlock = lastSyncBlock
|
|
}
|
|
|
|
let nodeEvents = await BatchEventsService.getBatchEvents({
|
|
fromBlock,
|
|
type: 'NewNullifier'
|
|
})
|
|
|
|
console.log({
|
|
nodeEvents: nodeEvents.length,
|
|
nodeBlock: nodeEvents && nodeEvents[nodeEvents.length - 1] ? nodeEvents[nodeEvents.length - 1].blockNumber : undefined
|
|
})
|
|
|
|
if (nodeEvents && nodeEvents.length) {
|
|
nodeEvents = nodeEvents.map(({ blockNumber, transactionHash, args }) => ({
|
|
blockNumber,
|
|
transactionHash,
|
|
nullifier: args.nullifier,
|
|
}))
|
|
|
|
events.push(...nodeEvents)
|
|
}
|
|
|
|
await saveEvents(fileName, events)
|
|
}
|
|
|
|
const main = async () => {
|
|
const chainId = ChainId.XDAI
|
|
|
|
const ethChainId = ChainId.MAINNET
|
|
|
|
const provider = getProvider(chainId)
|
|
|
|
const ethProvider = getProvider(ethChainId)
|
|
|
|
const { BatchEventsService } = getTornadoPool(chainId, provider)
|
|
|
|
const { BridgeEventsService } = getBridgeHelper(ethChainId, ethProvider)
|
|
|
|
console.log(`Connected with ${chainId}: (block: ${await provider.getBlockNumber()})`)
|
|
|
|
console.log(`Connected with ${ethChainId}: (block: ${await ethProvider.getBlockNumber()})`)
|
|
|
|
await syncAccounts(ethChainId, BridgeEventsService)
|
|
|
|
await syncCommitments(chainId, BatchEventsService)
|
|
|
|
await syncNullifiers(chainId, BatchEventsService)
|
|
}
|
|
main() |