forked from tornadocash/classic-ui
200 lines
5.0 KiB
JavaScript
200 lines
5.0 KiB
JavaScript
import { MerkleTree, PartialMerkleTree } from '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_${currency}_${amount}_bloom.json.gz`
|
|
})
|
|
}
|
|
|
|
getFileName(partNumber = trees.PARTS_COUNT) {
|
|
return `trees/deposits_${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.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()
|