mirror of
https://github.com/tornadocash/fixed-merkle-tree.git
synced 2024-11-21 17:27:08 +01:00
wip.
This commit is contained in:
parent
f2697487c8
commit
95ac6e0f55
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ build
|
||||
lib
|
||||
yarn-error.log
|
||||
.idea
|
||||
.nyc_output
|
||||
|
14
.run/Template Mocha.run.xml
Normal file
14
.run/Template Mocha.run.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="true" type="mocha-javascript-test-runner">
|
||||
<node-interpreter>project</node-interpreter>
|
||||
<node-options />
|
||||
<mocha-package>$PROJECT_DIR$/node_modules/ts-mocha</mocha-package>
|
||||
<working-directory>$PROJECT_DIR$</working-directory>
|
||||
<pass-parent-env>true</pass-parent-env>
|
||||
<ui>bdd</ui>
|
||||
<extra-mocha-options />
|
||||
<test-kind>PATTERN</test-kind>
|
||||
<test-pattern>$PROJECT_DIR$/test/*.spec.ts</test-pattern>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
238
src/fixedMerkleTree.ts
Normal file
238
src/fixedMerkleTree.ts
Normal file
@ -0,0 +1,238 @@
|
||||
import {
|
||||
defaultHash,
|
||||
Element,
|
||||
HashFunction,
|
||||
MerkleTreeOptions,
|
||||
ProofPath,
|
||||
SerializedTreeState,
|
||||
TreeEdge,
|
||||
} from './'
|
||||
|
||||
|
||||
export default class MerkleTree {
|
||||
get layers(): Array<Element[]> {
|
||||
return this._layers.slice()
|
||||
}
|
||||
|
||||
set layers(value: Array<Element[]>) {
|
||||
this._layers = value
|
||||
}
|
||||
|
||||
levels: number
|
||||
capacity: number
|
||||
private _hashFn: HashFunction<Element>
|
||||
private zeroElement: Element
|
||||
private _zeros: Element[]
|
||||
private _layers: Array<Element[]>
|
||||
|
||||
constructor(levels: number, elements: Element[] = [], {
|
||||
hashFunction = defaultHash,
|
||||
zeroElement = 0,
|
||||
}: MerkleTreeOptions = {}) {
|
||||
this.levels = levels
|
||||
this.capacity = 2 ** levels
|
||||
if (elements.length > this.capacity) {
|
||||
throw new Error('Tree is full')
|
||||
}
|
||||
this._hashFn = hashFunction
|
||||
this.zeroElement = zeroElement
|
||||
|
||||
this._layers = []
|
||||
this._layers[0] = elements.slice()
|
||||
this._buildZeros()
|
||||
this._rebuild()
|
||||
}
|
||||
|
||||
private _buildZeros() {
|
||||
this._zeros = [this.zeroElement]
|
||||
for (let i = 1; i <= this.levels; i++) {
|
||||
this._zeros[i] = this._hashFn(this._zeros[i - 1], this._zeros[i - 1])
|
||||
}
|
||||
}
|
||||
|
||||
_rebuild() {
|
||||
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++) {
|
||||
this._layers[level][i] = this._hashFn(
|
||||
this._layers[level - 1][i * 2],
|
||||
i * 2 + 1 < this._layers[level - 1].length
|
||||
? this._layers[level - 1][i * 2 + 1]
|
||||
: this._zeros[level - 1],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tree root
|
||||
*/
|
||||
root(): Element {
|
||||
return this._layers[this.levels][0] ?? this._zeros[this.levels]
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert new element into the tree
|
||||
* @param element Element to insert
|
||||
*/
|
||||
insert(element: Element) {
|
||||
if (this._layers[0].length >= this.capacity) {
|
||||
throw new Error('Tree is full')
|
||||
}
|
||||
this.update(this._layers[0].length, element)
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert multiple elements into the tree.
|
||||
* @param {Array} elements Elements to insert
|
||||
*/
|
||||
bulkInsert(elements: Element[]) {
|
||||
if (!elements.length) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._layers[0].length + elements.length > this.capacity) {
|
||||
throw new Error('Tree is full')
|
||||
}
|
||||
// First we insert all elements except the last one
|
||||
// updating only full subtree hashes (all layers where inserted element has odd index)
|
||||
// the last element will update the full path to the root making the tree consistent again
|
||||
for (let i = 0; i < elements.length - 1; i++) {
|
||||
this._layers[0].push(elements[i])
|
||||
let level = 0
|
||||
let index = this._layers[0].length - 1
|
||||
while (index % 2 === 1) {
|
||||
level++
|
||||
index >>= 1
|
||||
this._layers[level][index] = this._hashFn(
|
||||
this._layers[level - 1][index * 2],
|
||||
this._layers[level - 1][index * 2 + 1],
|
||||
)
|
||||
}
|
||||
}
|
||||
this.insert(elements[elements.length - 1])
|
||||
}
|
||||
|
||||
/**
|
||||
* Change an element in the tree
|
||||
* @param {number} index Index of element to change
|
||||
* @param element Updated element value
|
||||
*/
|
||||
update(index: number, element: Element) {
|
||||
if (isNaN(Number(index)) || index < 0 || index > this._layers[0].length || index >= this.capacity) {
|
||||
throw new Error('Insert index out of bounds: ' + index)
|
||||
}
|
||||
this._layers[0][index] = element
|
||||
for (let level = 1; level <= this.levels; level++) {
|
||||
index >>= 1
|
||||
this._layers[level][index] = this._hashFn(
|
||||
this._layers[level - 1][index * 2],
|
||||
index * 2 + 1 < this._layers[level - 1].length
|
||||
? this._layers[level - 1][index * 2 + 1]
|
||||
: this._zeros[level - 1],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get merkle path to a leaf
|
||||
* @param {number} index Leaf index to generate path for
|
||||
* @returns {{pathElements: Object[], pathIndex: number[]}} An object containing adjacent elements and left-right index
|
||||
*/
|
||||
path(index: Element): ProofPath {
|
||||
if (isNaN(Number(index)) || index < 0 || index >= this._layers[0].length) {
|
||||
throw new Error('Index out of bounds: ' + index)
|
||||
}
|
||||
let elIndex = +index
|
||||
const pathElements: Element[] = []
|
||||
const pathIndices: number[] = []
|
||||
const pathPositions: number [] = []
|
||||
for (let level = 0; level < this.levels; level++) {
|
||||
pathIndices[level] = elIndex % 2
|
||||
const leafIndex = elIndex ^ 1
|
||||
if (leafIndex < this._layers[level].length) {
|
||||
pathElements[level] = this._layers[level][leafIndex]
|
||||
pathPositions[level] = leafIndex
|
||||
} else {
|
||||
pathElements[level] = this._zeros[level]
|
||||
pathPositions[level] = 0
|
||||
}
|
||||
elIndex >>= 1
|
||||
}
|
||||
return {
|
||||
pathElements,
|
||||
pathIndices,
|
||||
pathPositions,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an element in the tree
|
||||
* @param element An element to find
|
||||
* @param comparator A function that checks leaf value equality
|
||||
* @returns {number} Index if element is found, otherwise -1
|
||||
*/
|
||||
indexOf(element: Element, comparator?: <T, R> (arg0: T, arg1: T) => R): number {
|
||||
if (comparator) {
|
||||
return this._layers[0].findIndex((el) => comparator<Element, number>(element, el))
|
||||
} else {
|
||||
return this._layers[0].indexOf(element)
|
||||
}
|
||||
}
|
||||
|
||||
getTreeEdge(edgeElement: Element, index?: number): TreeEdge {
|
||||
if (edgeElement === 'undefined') {
|
||||
throw new Error('element is required')
|
||||
}
|
||||
let edgeIndex: number
|
||||
if (!Number.isInteger(index)) {
|
||||
index = -1
|
||||
const leaves = this._layers[0]
|
||||
index = leaves.indexOf(edgeElement)
|
||||
edgeIndex = index
|
||||
}
|
||||
|
||||
if (index <= -1) {
|
||||
return null
|
||||
}
|
||||
const edgePath = this.path(index)
|
||||
return { edgePath, edgeElement, edgeIndex }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of non-zero tree elements.
|
||||
*/
|
||||
get elements() {
|
||||
return this._layers[0].slice()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of n-th zero elements array
|
||||
*/
|
||||
get zeros() {
|
||||
return this._zeros.slice()
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize entire tree state including intermediate layers into a plain object
|
||||
* Deserializing it back will not require to recompute any hashes
|
||||
* Elements are not converted to a plain type, this is responsibility of the caller
|
||||
*/
|
||||
serialize(): SerializedTreeState {
|
||||
return {
|
||||
levels: this.levels,
|
||||
_zeros: this._zeros,
|
||||
_layers: this._layers,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize data into a MerkleTree instance
|
||||
* Make sure to provide the same hashFunction as was used in the source tree,
|
||||
* otherwise the tree state will be invalid
|
||||
*/
|
||||
static deserialize(data: SerializedTreeState, hashFunction?: HashFunction<Element>): MerkleTree {
|
||||
return new MerkleTree(data.levels, data._layers[0], { hashFunction, zeroElement: data._zeros[0] })
|
||||
}
|
||||
}
|
||||
|
265
src/index.ts
265
src/index.ts
@ -1,260 +1,15 @@
|
||||
import simpleHash from './simpleHash'
|
||||
import { simpleHash } from './simpleHash'
|
||||
|
||||
const defaultHash = (left: Element, right: Element): string => simpleHash([left, right])
|
||||
export { default as MerkleTree } from './fixedMerkleTree'
|
||||
export { PartialMerkleTree } from './partialMerkleTree'
|
||||
export { simpleHash } from './simpleHash'
|
||||
|
||||
export default class MerkleTree {
|
||||
get layers(): Array<Element[]> {
|
||||
return this._layers.slice()
|
||||
}
|
||||
|
||||
set layers(value: Array<Element[]>) {
|
||||
this._layers = value
|
||||
}
|
||||
|
||||
levels: number
|
||||
capacity: number
|
||||
private _hashFn: HashFunction
|
||||
private zeroElement: Element
|
||||
private _zeros: Element[]
|
||||
private _layers: Array<Element[]>
|
||||
|
||||
constructor(levels: number, elements: Element[] = [], {
|
||||
hashFunction = defaultHash,
|
||||
zeroElement = 0,
|
||||
}: MerkleTreeOptions = {}) {
|
||||
this.levels = levels
|
||||
this.capacity = 2 ** levels
|
||||
if (elements.length > this.capacity) {
|
||||
throw new Error('Tree is full')
|
||||
}
|
||||
this._hashFn = hashFunction
|
||||
this.zeroElement = zeroElement
|
||||
this._zeros = []
|
||||
this._zeros[0] = zeroElement
|
||||
for (let i = 1; i <= levels; i++) {
|
||||
this._zeros[i] = this._hashFn(this._zeros[i - 1], this._zeros[i - 1])
|
||||
}
|
||||
this._layers = []
|
||||
this._layers[0] = elements.slice()
|
||||
this._rebuild()
|
||||
}
|
||||
|
||||
_rebuild() {
|
||||
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++) {
|
||||
this._layers[level][i] = this._hashFn(
|
||||
this._layers[level - 1][i * 2],
|
||||
i * 2 + 1 < this._layers[level - 1].length
|
||||
? this._layers[level - 1][i * 2 + 1]
|
||||
: this._zeros[level - 1],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tree root
|
||||
*/
|
||||
root(): string {
|
||||
return `${this._layers[this.levels].length > 0 ? this._layers[this.levels][0] : this._zeros[this.levels]}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert new element into the tree
|
||||
* @param element Element to insert
|
||||
*/
|
||||
insert(element: Element) {
|
||||
if (this._layers[0].length >= this.capacity) {
|
||||
throw new Error('Tree is full')
|
||||
}
|
||||
this.update(this._layers[0].length, element)
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert multiple elements into the tree.
|
||||
* @param {Array} elements Elements to insert
|
||||
*/
|
||||
bulkInsert(elements: Element[]) {
|
||||
if (!elements.length) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._layers[0].length + elements.length > this.capacity) {
|
||||
throw new Error('Tree is full')
|
||||
}
|
||||
// First we insert all elements except the last one
|
||||
// updating only full subtree hashes (all layers where inserted element has odd index)
|
||||
// the last element will update the full path to the root making the tree consistent again
|
||||
for (let i = 0; i < elements.length - 1; i++) {
|
||||
this._layers[0].push(elements[i])
|
||||
let level = 0
|
||||
let index = this._layers[0].length - 1
|
||||
while (index % 2 === 1) {
|
||||
level++
|
||||
index >>= 1
|
||||
this._layers[level][index] = this._hashFn(
|
||||
this._layers[level - 1][index * 2],
|
||||
this._layers[level - 1][index * 2 + 1],
|
||||
)
|
||||
}
|
||||
}
|
||||
this.insert(elements[elements.length - 1])
|
||||
}
|
||||
|
||||
/**
|
||||
* Change an element in the tree
|
||||
* @param {number} index Index of element to change
|
||||
* @param element Updated element value
|
||||
*/
|
||||
update(index: number, element: Element) {
|
||||
if (isNaN(Number(index)) || index < 0 || index > this._layers[0].length || index >= this.capacity) {
|
||||
throw new Error('Insert index out of bounds: ' + index)
|
||||
}
|
||||
this._layers[0][index] = element
|
||||
for (let level = 1; level <= this.levels; level++) {
|
||||
index >>= 1
|
||||
this._layers[level][index] = this._hashFn(
|
||||
this._layers[level - 1][index * 2],
|
||||
index * 2 + 1 < this._layers[level - 1].length
|
||||
? this._layers[level - 1][index * 2 + 1]
|
||||
: this._zeros[level - 1],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get merkle path to a leaf
|
||||
* @param {number} index Leaf index to generate path for
|
||||
* @returns {{pathElements: Object[], pathIndex: number[]}} An object containing adjacent elements and left-right index
|
||||
*/
|
||||
path(index: Element): ProofPath {
|
||||
if (isNaN(Number(index)) || index < 0 || index >= this._layers[0].length) {
|
||||
throw new Error('Index out of bounds: ' + index)
|
||||
}
|
||||
let elIndex = +index
|
||||
const pathElements: Element[] = []
|
||||
const pathIndices: number[] = []
|
||||
const pathPositions: number [] = []
|
||||
for (let level = 0; level < this.levels; level++) {
|
||||
pathIndices[level] = elIndex % 2
|
||||
const leafIndex = elIndex ^ 1
|
||||
if (leafIndex < this._layers[level].length) {
|
||||
pathElements[level] = this._layers[level][leafIndex]
|
||||
pathPositions[level] = leafIndex
|
||||
} else {
|
||||
pathElements[level] = this._zeros[level]
|
||||
pathPositions[level] = 0
|
||||
}
|
||||
elIndex >>= 1
|
||||
}
|
||||
return {
|
||||
pathElements,
|
||||
pathIndices,
|
||||
pathPositions,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an element in the tree
|
||||
* @param element An element to find
|
||||
* @param comparator A function that checks leaf value equality
|
||||
* @returns {number} Index if element is found, otherwise -1
|
||||
*/
|
||||
indexOf(element: Element, comparator?: <T, R> (arg0: T, arg1: T) => R): number {
|
||||
if (comparator) {
|
||||
return this._layers[0].findIndex((el) => comparator<Element, number>(element, el))
|
||||
} else {
|
||||
return this._layers[0].indexOf(element)
|
||||
}
|
||||
}
|
||||
|
||||
getTreeEdge(edgeElement: Element, index?: number) {
|
||||
if (edgeElement === 'undefined') {
|
||||
throw new Error('element is required')
|
||||
}
|
||||
let edgeIndex: number
|
||||
if (!Number.isInteger(index)) {
|
||||
index = -1
|
||||
const leaves = this._layers[0]
|
||||
index = leaves.indexOf(edgeElement)
|
||||
edgeIndex = index
|
||||
}
|
||||
|
||||
if (index <= -1) {
|
||||
return []
|
||||
}
|
||||
const edgePath = this.path(index)
|
||||
return { edgePath, edgeElement, edgeIndex }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of non-zero tree elements.
|
||||
*/
|
||||
get elements() {
|
||||
return this._layers[0].slice()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of n-th zero elements array
|
||||
*/
|
||||
get zeros() {
|
||||
return this._zeros.slice()
|
||||
}
|
||||
|
||||
getLayersAsObject() {
|
||||
const layers = this.layers
|
||||
const objs = []
|
||||
for (let i = 0; i < this.levels; i++) {
|
||||
const arr = []
|
||||
for (let j = 0; j < layers[i].length; j++) {
|
||||
const obj = { [layers[i][j]]: null }
|
||||
if (objs.length) {
|
||||
obj[layers[i][j]] = {}
|
||||
const a = objs.shift()
|
||||
const akey = Object.keys(a)[0]
|
||||
obj[layers[i][j]][akey] = a[akey]
|
||||
if (objs.length) {
|
||||
const b = objs.shift()
|
||||
const bkey = Object.keys(b)[0]
|
||||
obj[layers[i][j]][bkey] = b[bkey]
|
||||
}
|
||||
}
|
||||
arr.push(obj)
|
||||
}
|
||||
objs.push(...arr)
|
||||
}
|
||||
return objs[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize entire tree state including intermediate layers into a plain object
|
||||
* Deserializing it back will not require to recompute any hashes
|
||||
* Elements are not converted to a plain type, this is responsibility of the caller
|
||||
*/
|
||||
serialize(): SerializedTreeState {
|
||||
return {
|
||||
levels: this.levels,
|
||||
_zeros: this._zeros,
|
||||
_layers: this._layers,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize data into a MerkleTree instance
|
||||
* Make sure to provide the same hashFunction as was used in the source tree,
|
||||
* otherwise the tree state will be invalid
|
||||
*/
|
||||
static deserialize(data: SerializedTreeState, hashFunction?: HashFunction): MerkleTree {
|
||||
return new MerkleTree(data.levels, data._layers[0], { hashFunction, zeroElement: data._zeros[0] })
|
||||
}
|
||||
export type HashFunction<T> = {
|
||||
(left: T, right: T): string
|
||||
}
|
||||
|
||||
export type HashFunction = {
|
||||
(left: string | number, right: string | number): string
|
||||
}
|
||||
export type MerkleTreeOptions = {
|
||||
hashFunction?: HashFunction
|
||||
hashFunction?: HashFunction<Element>
|
||||
zeroElement?: Element
|
||||
}
|
||||
|
||||
@ -271,3 +26,9 @@ export type ProofPath = {
|
||||
pathIndices: number[],
|
||||
pathPositions: number[],
|
||||
}
|
||||
export type TreeEdge = {
|
||||
edgeElement: Element;
|
||||
edgePath: ProofPath;
|
||||
edgeIndex: number
|
||||
}
|
||||
export const defaultHash = (left: Element, right: Element): string => simpleHash([left, right])
|
||||
|
@ -1,27 +1,31 @@
|
||||
import { Element, HashFunction, ProofPath } from './index'
|
||||
import { defaultHash, Element, HashFunction, MerkleTreeOptions, ProofPath, TreeEdge } from './'
|
||||
|
||||
type LeafWithIndex = { index: number, data: Element }
|
||||
|
||||
export class PartialMerkleTree {
|
||||
levels: number
|
||||
private _hash: HashFunction
|
||||
private zeroElement: Element
|
||||
private _zeros: Element[]
|
||||
private _layers: Array<Element[]>
|
||||
private _leaves: Element[]
|
||||
private _leavesAfterEdge: Element[]
|
||||
private _edgeLeaf: LeafWithIndex
|
||||
private _root: string
|
||||
private _hashFn: HashFunction
|
||||
private _root: Element
|
||||
private _hashFn: HashFunction<Element>
|
||||
private _edgeLeafProof: ProofPath
|
||||
|
||||
constructor(edgeLeafProof: ProofPath, edgeLeaf: LeafWithIndex, leaves: Element[], root: string, hashFn: HashFunction) {
|
||||
this._edgeLeafProof = edgeLeafProof
|
||||
this._edgeLeaf = edgeLeaf
|
||||
constructor({
|
||||
edgePath,
|
||||
edgeElement,
|
||||
edgeIndex,
|
||||
}: TreeEdge, leaves: Element[], root: Element, { hashFunction, zeroElement }: MerkleTreeOptions = {}) {
|
||||
this._edgeLeafProof = edgePath
|
||||
this.zeroElement = zeroElement ?? 0
|
||||
this._edgeLeaf = { data: edgeElement, index: edgeIndex }
|
||||
this._leavesAfterEdge = leaves
|
||||
this._root = root
|
||||
this._hashFn = hashFn
|
||||
|
||||
this._hashFn = hashFunction || defaultHash
|
||||
this._buildTree()
|
||||
}
|
||||
|
||||
get capacity() {
|
||||
@ -32,15 +36,23 @@ export class PartialMerkleTree {
|
||||
const edgeLeafIndex = this._edgeLeaf.index
|
||||
this._leaves = [...Array.from({ length: edgeLeafIndex - 1 }, () => null), ...this._leavesAfterEdge]
|
||||
this._layers = [this._leaves]
|
||||
this._buildZeros()
|
||||
this._rebuild()
|
||||
|
||||
}
|
||||
|
||||
private _buildZeros() {
|
||||
this._zeros = [this.zeroElement]
|
||||
for (let i = 1; i <= this.levels; i++) {
|
||||
this._zeros[i] = this._hashFn(this._zeros[i - 1], this._zeros[i - 1])
|
||||
}
|
||||
}
|
||||
|
||||
_rebuild() {
|
||||
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++) {
|
||||
this._layers[level][i] = this._hash(
|
||||
this._layers[level][i] = this._hashFn(
|
||||
this._layers[level - 1][i * 2],
|
||||
i * 2 + 1 < this._layers[level - 1].length
|
||||
? this._layers[level - 1][i * 2 + 1]
|
||||
|
@ -5,7 +5,7 @@
|
||||
* @param hashLength
|
||||
*/
|
||||
|
||||
function simpleHash<T>(data: T[], seed?: number, hashLength = 40): string {
|
||||
export function simpleHash<T>(data: T[], seed?: number, hashLength = 40): string {
|
||||
const str = data.join('')
|
||||
let i, l,
|
||||
hval = seed ?? 0x811c9dcc5
|
||||
@ -17,4 +17,3 @@ function simpleHash<T>(data: T[], seed?: number, hashLength = 40): string {
|
||||
return BigInt('0x' + hash.padEnd(hashLength - (hash.length - 1), '0')).toString(10)
|
||||
}
|
||||
|
||||
export default simpleHash
|
||||
|
@ -1,4 +1,4 @@
|
||||
import MerkleTree from '../src'
|
||||
import { MerkleTree } from '../src'
|
||||
import { assert, should } from 'chai'
|
||||
import { it } from 'mocha'
|
||||
|
||||
@ -74,7 +74,7 @@ describe('MerkleTree', () => {
|
||||
should().equal(tree.root(), '4066635800770511602067209448381558554624')
|
||||
})
|
||||
|
||||
it('should give the same result as sequental inserts', () => {
|
||||
it('should give the same result as sequential inserts', () => {
|
||||
const initialArray = [
|
||||
[1],
|
||||
[1, 2],
|
||||
@ -278,6 +278,24 @@ describe('MerkleTree', () => {
|
||||
const layers2 = tree.layers
|
||||
should().not.equal(layers1, layers2)
|
||||
})
|
||||
it('should return correct zeros array', () => {
|
||||
const zeros = [
|
||||
0,
|
||||
'1390935134112885103361924701261056180224',
|
||||
'3223901263414086620636498663535535980544',
|
||||
'938972308169430750202858820582946897920',
|
||||
'3743880566844110745576746962917825445888',
|
||||
]
|
||||
const tree = new MerkleTree(4, [])
|
||||
assert.deepEqual(tree.zeros, zeros, 'Not equal')
|
||||
})
|
||||
it('should return copy of zeros array', () => {
|
||||
const tree = new MerkleTree(4, [])
|
||||
const zeros1 = tree.zeros
|
||||
tree.insert(6)
|
||||
const zeros2 = tree.zeros
|
||||
should().not.equal(zeros1, zeros2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#serialize', () => {
|
||||
|
19
test/partialMerkleTree.spec.ts
Normal file
19
test/partialMerkleTree.spec.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { MerkleTree, PartialMerkleTree } from '../src'
|
||||
import { assert, should } from 'chai'
|
||||
import { it } from 'mocha'
|
||||
|
||||
describe('PartialMerkleTree', () => {
|
||||
|
||||
describe('#constructor', () => {
|
||||
const leaves = [1, 2, 3, 4, 5]
|
||||
const fullTree = new MerkleTree(4, leaves)
|
||||
const root = fullTree.root()
|
||||
const edge = fullTree.getTreeEdge(3)
|
||||
const leavesAfterEdge = leaves.splice(edge.edgeIndex)
|
||||
it('should initialize merkle tree', () => {
|
||||
const partialTree = new PartialMerkleTree(edge, leavesAfterEdge, root)
|
||||
console.log(partialTree)
|
||||
return true
|
||||
})
|
||||
})
|
||||
})
|
@ -1,4 +1,4 @@
|
||||
import simpleHash from '../src/simpleHash'
|
||||
import { simpleHash } from '../src'
|
||||
import { it } from 'mocha'
|
||||
import { should } from 'chai'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user