final buildHashes algorithm

This commit is contained in:
Sergei SMART 2022-03-10 18:00:26 +10:00
parent 623cef3da2
commit 6439775e37
3 changed files with 21 additions and 96 deletions

@ -65,20 +65,6 @@ export default class MerkleTree {
} }
} }
// _buildHashes2(nodes: Element[]) {
// let layerIndex = 0
// while (layerIndex < this.levels) {
// layerIndex = this._layers.length
// this._layers[layerIndex] = []
// for (let i = 0; i < nodes.length; i += 2) {
// const left = nodes[i]
// const right = (i + 1 === nodes.length && nodes.length % 2 === 1) ? this._zeros[layerIndex - 1] : nodes[i + 1]
// this._layers[layerIndex].push(this._hashFn(left, right))
// }
// nodes = this._layers[layerIndex]
// }
// }
/** /**
* Get tree root * Get tree root
*/ */

@ -91,9 +91,7 @@ export class PartialMerkleTree {
} }
this._layers = [this._leaves] this._layers = [this._leaves]
this._buildZeros() this._buildZeros()
// this._buildHashes() this._buildHashes()
this._buildHashes3()
} }
private _buildZeros() { private _buildZeros() {
@ -104,70 +102,26 @@ export class PartialMerkleTree {
} }
_buildHashes() { _buildHashes() {
for (let level = 1; level <= this.levels; level++) {
this._layers[level] = []
for (let i = 0; i < Math.ceil(this._layers[level - 1].length / 2); i++) {
const left = this._layers[level - 1][i * 2]
const right = i * 2 + 1 < this._layers[level - 1].length
? this._layers[level - 1][i * 2 + 1]
: this._zeros[level - 1]
let hash: Element = this._hashFn(left, right)
if (!hash && this._edgeLeafProof.pathPositions[level] === i) hash = this._edgeLeafProof.pathElements[level]
if (level === this.levels) hash = hash || this._initialRoot
if (hash) this._layers[level][i] = hash
}
}
}
_buildHashes2() {
let index = this.edgeIndex
let nodes: Element[]
for (let layerIndex = 1; layerIndex <= this.levels; layerIndex++) { for (let layerIndex = 1; layerIndex <= this.levels; layerIndex++) {
nodes = this._layers[layerIndex - 1] const nodes = this._layers[layerIndex - 1]
this._layers[layerIndex] = [] const length = nodes.length
index = layerIndex > 1 ? Math.ceil(index / 2) : index let currentLength = Math.ceil(length / 2)
for (let i = 0; i < nodes.length; i += 2) { const currentLayer = new Array(currentLength)
const left = nodes[i] currentLength--
const right = (i + 1 < nodes.length) ? nodes[i + 1] : this._zeros[layerIndex - 1] const starFrom = length - ((length % 2) ^ 1)
let hash: Element = this._hashFn(left, right) let j = 0
if (layerIndex === this.levels) hash = hash || this._edgeLeafProof.pathRoot for (let i = starFrom; i >= 0; i -= 2) {
this._layers[layerIndex].push(hash) if (nodes[i - 1] === undefined) break
}
if (this._proofMap.has(layerIndex)) {
const [proofPos, proofEl] = this._proofMap.get(layerIndex)
this._layers[layerIndex][proofPos] = this._layers[layerIndex][proofPos] || proofEl
}
}
}
_buildHashes3() {
let edgeIndex = this.edgeIndex
let nodes: Element[]
for (let layerIndex = 1; layerIndex <= this.levels; layerIndex++) {
nodes = this._layers[layerIndex - 1]
this._layers[layerIndex] = []
edgeIndex = layerIndex > 1 ? Math.ceil(edgeIndex / 2) : edgeIndex
const from = nodes.length % 2 === 0 ? nodes.length - 1 : nodes.length
let i = from
for (i; i >= 0; i -= 2) {
if (i < edgeIndex - 2) {
break
}
const left = nodes[i - 1] const left = nodes[i - 1]
const right = (i === from && nodes.length % 2 === 1) ? this._zeros[layerIndex - 1] : nodes[i] const right = (i === starFrom && length % 2 === 1) ? this._zeros[layerIndex - 1] : nodes[i]
let hash: Element = this._hashFn(left, right) currentLayer[currentLength - j] = this._hashFn(left, right)
if (layerIndex === this.levels) hash = hash || this._edgeLeafProof.pathRoot j++
if (hash) this._layers[layerIndex].push(hash)
} }
this._layers[layerIndex].reverse()
const skipCount = (edgeIndex >> 1)
const emptyArray = nodes.length > 1 ? new Array(skipCount) : []
console.log(layerIndex, edgeIndex, skipCount, nodes.length)
this._layers[layerIndex] = emptyArray.concat(this._layers[layerIndex])
if (this._proofMap.has(layerIndex)) { if (this._proofMap.has(layerIndex)) {
const [proofPos, proofEl] = this._proofMap.get(layerIndex) const [proofPos, proofEl] = this._proofMap.get(layerIndex)
this._layers[layerIndex][proofPos] = this._layers[layerIndex][proofPos] || proofEl if (!currentLayer[proofPos]) currentLayer[proofPos] = proofEl
} }
this._layers[layerIndex] = currentLayer
} }
} }
@ -206,9 +160,7 @@ export class PartialMerkleTree {
index >>= 1 index >>= 1
const left = this._layers[level - 1][index * 2] const left = this._layers[level - 1][index * 2]
const right = this._layers[level - 1][index * 2 + 1] const right = this._layers[level - 1][index * 2 + 1]
let hash: Element = this._hashFn(left, right) this._layers[level][index] = this._hashFn(left, right)
if (!hash && this._edgeLeafProof.pathPositions[level] === i) hash = this._edgeLeafProof.pathElements[level]
this._layers[level][index] = hash
} }
} }
this.insert(elements[elements.length - 1]) this.insert(elements[elements.length - 1])
@ -227,26 +179,13 @@ export class PartialMerkleTree {
throw new Error(`Index ${index} is below the edge: ${this._edgeLeaf.index}`) throw new Error(`Index ${index} is below the edge: ${this._edgeLeaf.index}`)
} }
this._layers[0][index] = element this._layers[0][index] = element
for (let level = 1; level <= this.levels; level++) { for (let level = 1; level <= this.levels; level++) {
index >>= 1 index >>= 1
const left = this._layers[level - 1][index * 2] const left = this._layers[level - 1][index * 2]
const right = index * 2 + 1 < this._layers[level - 1].length const right = index * 2 + 1 < this._layers[level - 1].length
? this._layers[level - 1][index * 2 + 1] ? this._layers[level - 1][index * 2 + 1]
: this._zeros[level - 1] : this._zeros[level - 1]
const hash: Element = this._hashFn(left, right) this._layers[level][index] = this._hashFn(left, right)
// if (!hash && this._edgeLeafProof.pathPositions[level] === index * 2) {
// hash = this._edgeLeafProof.pathElements[level]
// }
if (this._proofMap.has(level)) {
const [proofPos, proofEl] = this._proofMap.get(level)
this._layers[level][proofPos] = this._layers[level][proofPos] || proofEl
}
// if (level === this.levels) {
// hash = hash || this._initialRoot
// }
this._layers[level][index] = hash
} }
} }
@ -257,7 +196,7 @@ export class PartialMerkleTree {
if (index < this._edgeLeaf.index) { if (index < this._edgeLeaf.index) {
throw new Error(`Index ${index} is below the edge: ${this._edgeLeaf.index}`) throw new Error(`Index ${index} is below the edge: ${this._edgeLeaf.index}`)
} }
let elIndex = +index let elIndex = Number(index)
const pathElements: Element[] = [] const pathElements: Element[] = []
const pathIndices: number[] = [] const pathIndices: number[] = []
const pathPositions: number [] = [] const pathPositions: number [] = []

@ -203,12 +203,12 @@ describe('PartialMerkleTree', () => {
describe('#path', () => { describe('#path', () => {
it('should return path for known nodes', () => { it('should return path for known nodes', () => {
const levels = 20 const levels = 10
const capacity = 2 ** levels const capacity = 2 ** levels
const elements = Array.from({ length: capacity / 2 }, (_, i) => i) const elements = Array.from({ length: capacity / 2 }, (_, i) => i)
const { fullTree, partialTree } = getTestTrees(levels, elements, 250) const { fullTree, partialTree } = getTestTrees(levels, elements, 250)
assert.deepEqual(fullTree.path(250), partialTree.path(250)) assert.deepEqual(fullTree.path(251), partialTree.path(251))
}).timeout(10000) }).timeout(1000)
it('should fail on incorrect index', () => { it('should fail on incorrect index', () => {
const { partialTree } = getTestTrees(10, [1, 2, 3, 4, 5, 6, 7, 8, 9], 4) const { partialTree } = getTestTrees(10, [1, 2, 3, 4, 5, 6, 7, 8, 9], 4)