import { MerkleTree, PartialMerkleTree } from '@tornado/fixed-merkle-tree' import { trees } from '@/constants' import { download } from '@/store/snark' import networkConfig from '@/networkConfig' import { mimc, bloomService } from '@/services' const supportedCaches = ['1', '56', '100', '137'] class MerkleTreeService { constructor({ netId, amount, currency, commitment, instanceName }) { this.netId = netId this.amount = amount this.currency = currency this.commitment = commitment this.instanceName = instanceName this.idb = window.$nuxt.$indexedDB(netId) this.bloomService = bloomService({ netId, amount, commitment, instanceName, fileFolder: 'trees', fileName: `deposits_${netId}_${currency}_${amount}_bloom.json.gz` }) } getFileName(partNumber = trees.PARTS_COUNT) { return `trees/deposits_${this.netId}_${this.currency}_${this.amount}_slice${partNumber}.json.gz` } createTree({ events }) { const { merkleTreeHeight, emptyElement } = networkConfig[`netId${this.netId}`] return new MerkleTree(merkleTreeHeight, events, { zeroElement: emptyElement, hashFunction: mimc.hash }) } async downloadEdge(name) { const slicedEdge = await download({ name, eventName: 'trees', contentType: 'string' }) if (!slicedEdge) { throw new Error('Cant download file') } return JSON.parse(slicedEdge) } createPartialTree({ edge, elements }) { const { emptyElement } = networkConfig[`netId${this.netId}`] return new PartialMerkleTree(trees.LEVELS, edge, elements, { zeroElement: emptyElement, hashFunction: mimc.hash }) } async getTreeFromCache() { try { const initialEdge = await this.downloadEdge(this.getFileName()) const partialTree = this.createPartialTree(initialEdge) if (initialEdge.elements.includes(this.commitment)) { return partialTree } const isCacheHasCommitment = await this.bloomService.checkBloom() if (!isCacheHasCommitment) { return partialTree } let edge let elements = [] for (let i = trees.PARTS_COUNT - 1; i > 0; i--) { const slicedEdge = await this.downloadEdge(this.getFileName(i)) edge = slicedEdge.edge elements = [].concat(slicedEdge.elements, elements) if (slicedEdge.elements.includes(this.commitment)) { break } } partialTree.shiftEdge(edge, elements) return partialTree } catch (err) { return undefined } } async getTreeFromDB() { try { const stringifyCachedTree = await this.idb.getAll({ storeName: `stringify_tree_${this.instanceName}` }) if (!stringifyCachedTree || !stringifyCachedTree.length) { return undefined } const [{ tree }] = stringifyCachedTree const parsedTree = JSON.parse(tree) const isPartial = '_edgeLeaf' in parsedTree const savedTree = isPartial ? PartialMerkleTree.deserialize(parsedTree, mimc.hash) : MerkleTree.deserialize(parsedTree, mimc.hash) if (isPartial) { const edgeIndex = savedTree.edgeIndex const indexOfEvent = savedTree.indexOf(this.commitment) // ToDo save edges mapping { edgeIndex, edgeSlice } if (indexOfEvent === -1 && edgeIndex !== 0) { const isCacheHasCommitment = await this.bloomService.checkBloom() if (isCacheHasCommitment) { let edge let elements = [] for (let i = trees.PARTS_COUNT; i > 0; i--) { const slicedEdge = await this.downloadEdge(this.getFileName(i)) if (edgeIndex > slicedEdge.edge.edgeIndex) { edge = slicedEdge.edge elements = [].concat(slicedEdge.elements, elements) } if (slicedEdge.elements.includes(this.commitment)) { break } } savedTree.shiftEdge(edge, elements) } } } return savedTree } catch (err) { return undefined } } async getTree() { const hasCache = supportedCaches.includes(this.netId.toString()) let cachedTree = await this.getTreeFromDB() if (!cachedTree && hasCache) { cachedTree = await this.getTreeFromCache() } return cachedTree } async saveTree({ tree }) { try { await this.idb.putItem({ storeName: `stringify_tree_${this.instanceName}`, data: { hashTree: '1', // need for replace tree tree: tree.toString() }, key: 'hashTree' }) } catch (err) { console.error('saveCachedTree has error:', err.message) } } } class TreesFactory { instances = new Map() getService = (payload) => { const instanceName = `${payload.netId}_${payload.currency}_${payload.amount}` if (this.instances.has(instanceName)) { return this.instances.get(instanceName) } const instance = new MerkleTreeService(payload) this.instances.set(instanceName, instance) return instance } } export const treesInterface = new TreesFactory()