diff --git a/docs/classes/_src_base_.base.md b/docs/classes/_src_base_.base.md index dee8c3f..6d5e862 100644 --- a/docs/classes/_src_base_.base.md +++ b/docs/classes/_src_base_.base.md @@ -6,6 +6,8 @@ * **Base** + ↳ [IncrementalMerkleTree](_src_incrementalmerkletree_.incrementalmerkletree.md) + ↳ [MerkleMountainRange](_src_merklemountainrange_.merklemountainrange.md) ↳ [MerkleTree](_src_merkletree_.merkletree.md) @@ -14,17 +16,17 @@ ### Methods -* [_bufferIndexOf](_src_base_.base.md#protected-_bufferindexof) -* [_isHexString](_src_base_.base.md#protected-_ishexstring) -* [_log2](_src_base_.base.md#protected-_log2) -* [_zip](_src_base_.base.md#protected-_zip) * [bigNumberify](_src_base_.base.md#bignumberify) * [binarySearch](_src_base_.base.md#binarysearch) +* [bufferIndexOf](_src_base_.base.md#protected-bufferindexof) * [bufferToHex](_src_base_.base.md#buffertohex) * [bufferify](_src_base_.base.md#bufferify) * [bufferifyFn](_src_base_.base.md#bufferifyfn) +* [isHexString](_src_base_.base.md#protected-ishexstring) * [linearSearch](_src_base_.base.md#linearsearch) +* [log2](_src_base_.base.md#protected-log2) * [print](_src_base_.base.md#print) +* [zip](_src_base_.base.md#protected-zip) * [bigNumberify](_src_base_.base.md#static-bignumberify) * [binarySearch](_src_base_.base.md#static-binarysearch) * [bufferToHex](_src_base_.base.md#static-buffertohex) @@ -36,99 +38,6 @@ ## Methods -### `Protected` _bufferIndexOf - -▸ **_bufferIndexOf**(`array`: Buffer[], `element`: Buffer, `isSorted`: boolean): *number* - -bufferIndexOf - -**`desc`** Returns the first index of which given buffer is found in array. - -**`example`** -```js -const index = tree.bufferIndexOf(haystack, needle) -``` - -**Parameters:** - -Name | Type | Default | ------- | ------ | ------ | -`array` | Buffer[] | - | -`element` | Buffer | - | -`isSorted` | boolean | false | - -**Returns:** *number* - -- Index number - -___ - -### `Protected` _isHexString - -▸ **_isHexString**(`value`: string): *boolean* - -isHexString - -**`desc`** Returns true if value is a hex string. - -**`example`** -```js -console.log(MerkleTree.isHexString('0x1234')) -``` - -**Parameters:** - -Name | Type | ------- | ------ | -`value` | string | - -**Returns:** *boolean* - -___ - -### `Protected` _log2 - -▸ **_log2**(`n`: number): *number* - -log2 - -**`desc`** Returns the log2 of number. - -**Parameters:** - -Name | Type | ------- | ------ | -`n` | number | - -**Returns:** *number* - -___ - -### `Protected` _zip - -▸ **_zip**(`a`: any[], `b`: any[]): *any[][]* - -zip - -**`desc`** Returns true if value is a hex string. - -**`example`** -```js -const zipped = tree.zip(['a', 'b'],['A', 'B']) -console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ] -``` - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`a` | any[] | first array | -`b` | any[] | second array | - -**Returns:** *any[][]* - -___ - ### bigNumberify ▸ **bigNumberify**(`value`: any): *BigInt* @@ -183,6 +92,33 @@ Name | Type | ___ +### `Protected` bufferIndexOf + +▸ **bufferIndexOf**(`array`: Buffer[], `element`: Buffer, `isSorted`: boolean): *number* + +bufferIndexOf + +**`desc`** Returns the first index of which given buffer is found in array. + +**`example`** +```js +const index = tree.bufferIndexOf(haystack, needle) +``` + +**Parameters:** + +Name | Type | Default | +------ | ------ | ------ | +`array` | Buffer[] | - | +`element` | Buffer | - | +`isSorted` | boolean | false | + +**Returns:** *number* + +- Index number + +___ + ### bufferToHex ▸ **bufferToHex**(`value`: Buffer, `withPrefix`: boolean): *string* @@ -253,6 +189,29 @@ Name | Type | ___ +### `Protected` isHexString + +▸ **isHexString**(`value`: string): *boolean* + +isHexString + +**`desc`** Returns true if value is a hex string. + +**`example`** +```js +console.log(MerkleTree.isHexString('0x1234')) +``` + +**Parameters:** + +Name | Type | +------ | ------ | +`value` | string | + +**Returns:** *boolean* + +___ + ### linearSearch ▸ **linearSearch**(`array`: Buffer[], `element`: Buffer, `eqChecker`: function): *number* @@ -293,6 +252,24 @@ Name | Type | ___ +### `Protected` log2 + +▸ **log2**(`n`: number): *number* + +log2 + +**`desc`** Returns the log2 of number. + +**Parameters:** + +Name | Type | +------ | ------ | +`n` | number | + +**Returns:** *number* + +___ + ### print ▸ **print**(): *void* @@ -310,6 +287,31 @@ tree.print() ___ +### `Protected` zip + +▸ **zip**(`a`: any[], `b`: any[]): *any[][]* + +zip + +**`desc`** Returns true if value is a hex string. + +**`example`** +```js +const zipped = tree.zip(['a', 'b'],['A', 'B']) +console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ] +``` + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`a` | any[] | first array | +`b` | any[] | second array | + +**Returns:** *any[][]* + +___ + ### `Static` bigNumberify ▸ **bigNumberify**(`value`: any): *BigInt* diff --git a/docs/classes/_src_incrementalmerkletree_.incrementalmerkletree.md b/docs/classes/_src_incrementalmerkletree_.incrementalmerkletree.md new file mode 100644 index 0000000..7897f01 --- /dev/null +++ b/docs/classes/_src_incrementalmerkletree_.incrementalmerkletree.md @@ -0,0 +1,780 @@ +[merkletreejs](../README.md) › [Globals](../globals.md) › ["src/IncrementalMerkleTree"](../modules/_src_incrementalmerkletree_.md) › [IncrementalMerkleTree](_src_incrementalmerkletree_.incrementalmerkletree.md) + +# Class: IncrementalMerkleTree + +## Hierarchy + +* [Base](_src_base_.base.md) + + ↳ **IncrementalMerkleTree** + +## Index + +### Constructors + +* [constructor](_src_incrementalmerkletree_.incrementalmerkletree.md#constructor) + +### Methods + +* [bigNumberify](_src_incrementalmerkletree_.incrementalmerkletree.md#bignumberify) +* [binarySearch](_src_incrementalmerkletree_.incrementalmerkletree.md#binarysearch) +* [bufferIndexOf](_src_incrementalmerkletree_.incrementalmerkletree.md#protected-bufferindexof) +* [bufferToHex](_src_incrementalmerkletree_.incrementalmerkletree.md#buffertohex) +* [bufferify](_src_incrementalmerkletree_.incrementalmerkletree.md#bufferify) +* [bufferifyFn](_src_incrementalmerkletree_.incrementalmerkletree.md#bufferifyfn) +* [computeRoot](_src_incrementalmerkletree_.incrementalmerkletree.md#computeroot) +* [copyList](_src_incrementalmerkletree_.incrementalmerkletree.md#copylist) +* [delete](_src_incrementalmerkletree_.incrementalmerkletree.md#delete) +* [getArity](_src_incrementalmerkletree_.incrementalmerkletree.md#getarity) +* [getDepth](_src_incrementalmerkletree_.incrementalmerkletree.md#getdepth) +* [getHexLayers](_src_incrementalmerkletree_.incrementalmerkletree.md#gethexlayers) +* [getHexRoot](_src_incrementalmerkletree_.incrementalmerkletree.md#gethexroot) +* [getLayers](_src_incrementalmerkletree_.incrementalmerkletree.md#getlayers) +* [getLayersAsObject](_src_incrementalmerkletree_.incrementalmerkletree.md#getlayersasobject) +* [getLeaves](_src_incrementalmerkletree_.incrementalmerkletree.md#getleaves) +* [getMaxLeaves](_src_incrementalmerkletree_.incrementalmerkletree.md#getmaxleaves) +* [getProof](_src_incrementalmerkletree_.incrementalmerkletree.md#getproof) +* [getRoot](_src_incrementalmerkletree_.incrementalmerkletree.md#getroot) +* [indexOf](_src_incrementalmerkletree_.incrementalmerkletree.md#indexof) +* [insert](_src_incrementalmerkletree_.incrementalmerkletree.md#insert) +* [isHexString](_src_incrementalmerkletree_.incrementalmerkletree.md#protected-ishexstring) +* [linearSearch](_src_incrementalmerkletree_.incrementalmerkletree.md#linearsearch) +* [log2](_src_incrementalmerkletree_.incrementalmerkletree.md#protected-log2) +* [print](_src_incrementalmerkletree_.incrementalmerkletree.md#print) +* [toString](_src_incrementalmerkletree_.incrementalmerkletree.md#tostring) +* [toTreeString](_src_incrementalmerkletree_.incrementalmerkletree.md#protected-totreestring) +* [update](_src_incrementalmerkletree_.incrementalmerkletree.md#update) +* [verify](_src_incrementalmerkletree_.incrementalmerkletree.md#verify) +* [zip](_src_incrementalmerkletree_.incrementalmerkletree.md#protected-zip) +* [bigNumberify](_src_incrementalmerkletree_.incrementalmerkletree.md#static-bignumberify) +* [binarySearch](_src_incrementalmerkletree_.incrementalmerkletree.md#static-binarysearch) +* [bufferToHex](_src_incrementalmerkletree_.incrementalmerkletree.md#static-buffertohex) +* [bufferify](_src_incrementalmerkletree_.incrementalmerkletree.md#static-bufferify) +* [hexZeroPad](_src_incrementalmerkletree_.incrementalmerkletree.md#static-hexzeropad) +* [isHexString](_src_incrementalmerkletree_.incrementalmerkletree.md#static-ishexstring) +* [linearSearch](_src_incrementalmerkletree_.incrementalmerkletree.md#static-linearsearch) +* [print](_src_incrementalmerkletree_.incrementalmerkletree.md#static-print) + +## Constructors + +### constructor + +\+ **new IncrementalMerkleTree**(`hashFn`: any, `options`: [Options](../interfaces/_src_incrementalmerkletree_.options.md)): *[IncrementalMerkleTree](_src_incrementalmerkletree_.incrementalmerkletree.md)* + +**Parameters:** + +Name | Type | +------ | ------ | +`hashFn` | any | +`options` | [Options](../interfaces/_src_incrementalmerkletree_.options.md) | + +**Returns:** *[IncrementalMerkleTree](_src_incrementalmerkletree_.incrementalmerkletree.md)* + +## Methods + +### bigNumberify + +▸ **bigNumberify**(`value`: any): *BigInt* + +*Inherited from [Base](_src_base_.base.md).[bigNumberify](_src_base_.base.md#bignumberify)* + +**Parameters:** + +Name | Type | +------ | ------ | +`value` | any | + +**Returns:** *BigInt* + +___ + +### binarySearch + +▸ **binarySearch**(`array`: Buffer[], `element`: Buffer, `compareFunction`: function): *number* + +*Inherited from [Base](_src_base_.base.md).[binarySearch](_src_base_.base.md#static-binarysearch)* + +binarySearch + +**`desc`** Returns the first index of which given item is found in array using binary search. + +**`example`** +```js +const index = tree.binarySearch(array, element, Buffer.compare) +``` + +**Parameters:** + +▪ **array**: *Buffer[]* + +Array of items. + +▪ **element**: *Buffer* + +Item to find. + +▪ **compareFunction**: *function* + +▸ (`a`: unknown, `b`: unknown): *number* + +**Parameters:** + +Name | Type | +------ | ------ | +`a` | unknown | +`b` | unknown | + +**Returns:** *number* + +- Index number + +___ + +### `Protected` bufferIndexOf + +▸ **bufferIndexOf**(`array`: Buffer[], `element`: Buffer, `isSorted`: boolean): *number* + +*Inherited from [Base](_src_base_.base.md).[bufferIndexOf](_src_base_.base.md#protected-bufferindexof)* + +bufferIndexOf + +**`desc`** Returns the first index of which given buffer is found in array. + +**`example`** +```js +const index = tree.bufferIndexOf(haystack, needle) +``` + +**Parameters:** + +Name | Type | Default | +------ | ------ | ------ | +`array` | Buffer[] | - | +`element` | Buffer | - | +`isSorted` | boolean | false | + +**Returns:** *number* + +- Index number + +___ + +### bufferToHex + +▸ **bufferToHex**(`value`: Buffer, `withPrefix`: boolean): *string* + +*Inherited from [Base](_src_base_.base.md).[bufferToHex](_src_base_.base.md#buffertohex)* + +bufferToHex + +**`desc`** Returns a hex string with 0x prefix for given buffer. + +**`example`** +```js +const hexStr = tree.bufferToHex(Buffer.from('A')) +``` + +**Parameters:** + +Name | Type | Default | +------ | ------ | ------ | +`value` | Buffer | - | +`withPrefix` | boolean | true | + +**Returns:** *string* + +___ + +### bufferify + +▸ **bufferify**(`value`: any): *Buffer* + +*Inherited from [Base](_src_base_.base.md).[bufferify](_src_base_.base.md#static-bufferify)* + +bufferify + +**`desc`** Returns a buffer type for the given value. + +**`example`** +```js +const buf = tree.bufferify('0x1234') +``` + +**Parameters:** + +Name | Type | +------ | ------ | +`value` | any | + +**Returns:** *Buffer* + +___ + +### bufferifyFn + +▸ **bufferifyFn**(`f`: any): *any* + +*Inherited from [Base](_src_base_.base.md).[bufferifyFn](_src_base_.base.md#bufferifyfn)* + +bufferifyFn + +**`desc`** Returns a function that will bufferify the return value. + +**`example`** +```js +const fn = tree.bufferifyFn((value) => sha256(value)) +``` + +**Parameters:** + +Name | Type | +------ | ------ | +`f` | any | + +**Returns:** *any* + +___ + +### computeRoot + +▸ **computeRoot**(): *any* + +**Returns:** *any* + +___ + +### copyList + +▸ **copyList**(`list`: any[]): *bigint[]* + +**Parameters:** + +Name | Type | +------ | ------ | +`list` | any[] | + +**Returns:** *bigint[]* + +___ + +### delete + +▸ **delete**(`index`: number): *void* + +**Parameters:** + +Name | Type | +------ | ------ | +`index` | number | + +**Returns:** *void* + +___ + +### getArity + +▸ **getArity**(): *number* + +**Returns:** *number* + +___ + +### getDepth + +▸ **getDepth**(): *number* + +**Returns:** *number* + +___ + +### getHexLayers + +▸ **getHexLayers**(): *string[]* + +**Returns:** *string[]* + +___ + +### getHexRoot + +▸ **getHexRoot**(): *string* + +**Returns:** *string* + +___ + +### getLayers + +▸ **getLayers**(): *any[]* + +**Returns:** *any[]* + +___ + +### getLayersAsObject + +▸ **getLayersAsObject**(): *any* + +**Returns:** *any* + +___ + +### getLeaves + +▸ **getLeaves**(): *bigint[]* + +**Returns:** *bigint[]* + +___ + +### getMaxLeaves + +▸ **getMaxLeaves**(): *number* + +**Returns:** *number* + +___ + +### getProof + +▸ **getProof**(`index`: number): *any* + +**Parameters:** + +Name | Type | +------ | ------ | +`index` | number | + +**Returns:** *any* + +___ + +### getRoot + +▸ **getRoot**(): *any* + +**Returns:** *any* + +___ + +### indexOf + +▸ **indexOf**(`leaf`: any): *number* + +**Parameters:** + +Name | Type | +------ | ------ | +`leaf` | any | + +**Returns:** *number* + +___ + +### insert + +▸ **insert**(`leaf`: any): *void* + +**Parameters:** + +Name | Type | +------ | ------ | +`leaf` | any | + +**Returns:** *void* + +___ + +### `Protected` isHexString + +▸ **isHexString**(`value`: string): *boolean* + +*Inherited from [Base](_src_base_.base.md).[isHexString](_src_base_.base.md#static-ishexstring)* + +isHexString + +**`desc`** Returns true if value is a hex string. + +**`example`** +```js +console.log(MerkleTree.isHexString('0x1234')) +``` + +**Parameters:** + +Name | Type | +------ | ------ | +`value` | string | + +**Returns:** *boolean* + +___ + +### linearSearch + +▸ **linearSearch**(`array`: Buffer[], `element`: Buffer, `eqChecker`: function): *number* + +*Inherited from [Base](_src_base_.base.md).[linearSearch](_src_base_.base.md#static-linearsearch)* + +linearSearch + +**`desc`** Returns the first index of which given item is found in array using linear search. + +**`example`** +```js +const index = tree.linearSearch(array, element, (a, b) => a === b) +``` + +**Parameters:** + +▪ **array**: *Buffer[]* + +Array of items. + +▪ **element**: *Buffer* + +Item to find. + +▪ **eqChecker**: *function* + +▸ (`a`: unknown, `b`: unknown): *boolean* + +**Parameters:** + +Name | Type | +------ | ------ | +`a` | unknown | +`b` | unknown | + +**Returns:** *number* + +- Index number + +___ + +### `Protected` log2 + +▸ **log2**(`n`: number): *number* + +*Inherited from [Base](_src_base_.base.md).[log2](_src_base_.base.md#protected-log2)* + +log2 + +**`desc`** Returns the log2 of number. + +**Parameters:** + +Name | Type | +------ | ------ | +`n` | number | + +**Returns:** *number* + +___ + +### print + +▸ **print**(): *void* + +*Inherited from [Base](_src_base_.base.md).[print](_src_base_.base.md#print)* + +print + +**`desc`** Prints out a visual representation of the merkle tree. + +**`example`** +```js +tree.print() +``` + +**Returns:** *void* + +___ + +### toString + +▸ **toString**(): *string* + +**Returns:** *string* + +___ + +### `Protected` toTreeString + +▸ **toTreeString**(): *string* + +**Returns:** *string* + +___ + +### update + +▸ **update**(`index`: number, `newLeaf`: any): *void* + +**Parameters:** + +Name | Type | +------ | ------ | +`index` | number | +`newLeaf` | any | + +**Returns:** *void* + +___ + +### verify + +▸ **verify**(`proof`: any): *boolean* + +**Parameters:** + +Name | Type | +------ | ------ | +`proof` | any | + +**Returns:** *boolean* + +___ + +### `Protected` zip + +▸ **zip**(`a`: any[], `b`: any[]): *any[][]* + +*Inherited from [Base](_src_base_.base.md).[zip](_src_base_.base.md#protected-zip)* + +zip + +**`desc`** Returns true if value is a hex string. + +**`example`** +```js +const zipped = tree.zip(['a', 'b'],['A', 'B']) +console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ] +``` + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`a` | any[] | first array | +`b` | any[] | second array | + +**Returns:** *any[][]* + +___ + +### `Static` bigNumberify + +▸ **bigNumberify**(`value`: any): *BigInt* + +*Inherited from [Base](_src_base_.base.md).[bigNumberify](_src_base_.base.md#bignumberify)* + +**Parameters:** + +Name | Type | +------ | ------ | +`value` | any | + +**Returns:** *BigInt* + +___ + +### `Static` binarySearch + +▸ **binarySearch**(`array`: Buffer[], `element`: Buffer, `compareFunction`: function): *number* + +*Inherited from [Base](_src_base_.base.md).[binarySearch](_src_base_.base.md#static-binarysearch)* + +binarySearch + +**`desc`** Returns the first index of which given item is found in array using binary search. + +**`example`** +```js +const index = MerkleTree.binarySearch(array, element, Buffer.compare) +``` + +**Parameters:** + +▪ **array**: *Buffer[]* + +Array of items. + +▪ **element**: *Buffer* + +Item to find. + +▪ **compareFunction**: *function* + +▸ (`a`: unknown, `b`: unknown): *number* + +**Parameters:** + +Name | Type | +------ | ------ | +`a` | unknown | +`b` | unknown | + +**Returns:** *number* + +- Index number + +___ + +### `Static` bufferToHex + +▸ **bufferToHex**(`value`: Buffer, `withPrefix`: boolean): *string* + +*Inherited from [Base](_src_base_.base.md).[bufferToHex](_src_base_.base.md#buffertohex)* + +bufferToHex + +**`desc`** Returns a hex string with 0x prefix for given buffer. + +**`example`** +```js +const hexStr = MerkleTree.bufferToHex(Buffer.from('A')) +``` + +**Parameters:** + +Name | Type | Default | +------ | ------ | ------ | +`value` | Buffer | - | +`withPrefix` | boolean | true | + +**Returns:** *string* + +___ + +### `Static` bufferify + +▸ **bufferify**(`value`: any): *Buffer* + +*Inherited from [Base](_src_base_.base.md).[bufferify](_src_base_.base.md#static-bufferify)* + +bufferify + +**`desc`** Returns a buffer type for the given value. + +**`example`** +```js +const buf = MerkleTree.bufferify('0x1234') +``` + +**Parameters:** + +Name | Type | +------ | ------ | +`value` | any | + +**Returns:** *Buffer* + +___ + +### `Static` hexZeroPad + +▸ **hexZeroPad**(`hexStr`: string, `length`: number): *string* + +*Inherited from [Base](_src_base_.base.md).[hexZeroPad](_src_base_.base.md#static-hexzeropad)* + +**Parameters:** + +Name | Type | +------ | ------ | +`hexStr` | string | +`length` | number | + +**Returns:** *string* + +___ + +### `Static` isHexString + +▸ **isHexString**(`v`: string): *boolean* + +*Inherited from [Base](_src_base_.base.md).[isHexString](_src_base_.base.md#static-ishexstring)* + +isHexString + +**`desc`** Returns true if value is a hex string. + +**`example`** +```js +console.log(MerkleTree.isHexString('0x1234')) +``` + +**Parameters:** + +Name | Type | +------ | ------ | +`v` | string | + +**Returns:** *boolean* + +___ + +### `Static` linearSearch + +▸ **linearSearch**(`array`: Buffer[], `element`: Buffer, `eqChecker`: function): *number* + +*Inherited from [Base](_src_base_.base.md).[linearSearch](_src_base_.base.md#static-linearsearch)* + +linearSearch + +**`desc`** Returns the first index of which given item is found in array using linear search. + +**`example`** +```js +const index = MerkleTree.linearSearch(array, element, (a, b) => a === b) +``` + +**Parameters:** + +▪ **array**: *Buffer[]* + +Array of items. + +▪ **element**: *Buffer* + +Item to find. + +▪ **eqChecker**: *function* + +▸ (`a`: unknown, `b`: unknown): *boolean* + +**Parameters:** + +Name | Type | +------ | ------ | +`a` | unknown | +`b` | unknown | + +**Returns:** *number* + +- Index number + +___ + +### `Static` print + +▸ **print**(`tree`: any): *void* + +*Inherited from [Base](_src_base_.base.md).[print](_src_base_.base.md#print)* + +print + +**`desc`** Prints out a visual representation of the given merkle tree. + +**`example`** +```js +MerkleTree.print(tree) +``` + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`tree` | any | Merkle tree instance. | + +**Returns:** *void* diff --git a/docs/classes/_src_merklemountainrange_.merklemountainrange.md b/docs/classes/_src_merklemountainrange_.merklemountainrange.md index 47e351d..7bfd485 100644 --- a/docs/classes/_src_merklemountainrange_.merklemountainrange.md +++ b/docs/classes/_src_merklemountainrange_.merklemountainrange.md @@ -29,13 +29,10 @@ ### Methods -* [_bufferIndexOf](_src_merklemountainrange_.merklemountainrange.md#protected-_bufferindexof) -* [_isHexString](_src_merklemountainrange_.merklemountainrange.md#protected-_ishexstring) -* [_log2](_src_merklemountainrange_.merklemountainrange.md#protected-_log2) -* [_zip](_src_merklemountainrange_.merklemountainrange.md#protected-_zip) * [append](_src_merklemountainrange_.merklemountainrange.md#append) * [bigNumberify](_src_merklemountainrange_.merklemountainrange.md#bignumberify) * [binarySearch](_src_merklemountainrange_.merklemountainrange.md#binarysearch) +* [bufferIndexOf](_src_merklemountainrange_.merklemountainrange.md#protected-bufferindexof) * [bufferToHex](_src_merklemountainrange_.merklemountainrange.md#buffertohex) * [bufferify](_src_merklemountainrange_.merklemountainrange.md#bufferify) * [bufferifyFn](_src_merklemountainrange_.merklemountainrange.md#bufferifyfn) @@ -51,8 +48,10 @@ * [hashBranch](_src_merklemountainrange_.merklemountainrange.md#hashbranch) * [hashLeaf](_src_merklemountainrange_.merklemountainrange.md#hashleaf) * [heightAt](_src_merklemountainrange_.merklemountainrange.md#heightat) +* [isHexString](_src_merklemountainrange_.merklemountainrange.md#protected-ishexstring) * [isLeaf](_src_merklemountainrange_.merklemountainrange.md#isleaf) * [linearSearch](_src_merklemountainrange_.merklemountainrange.md#linearsearch) +* [log2](_src_merklemountainrange_.merklemountainrange.md#protected-log2) * [mountainHeight](_src_merklemountainrange_.merklemountainrange.md#mountainheight) * [numOfPeaks](_src_merklemountainrange_.merklemountainrange.md#numofpeaks) * [peakBagging](_src_merklemountainrange_.merklemountainrange.md#peakbagging) @@ -62,6 +61,7 @@ * [print](_src_merklemountainrange_.merklemountainrange.md#print) * [rollUp](_src_merklemountainrange_.merklemountainrange.md#rollup) * [verify](_src_merklemountainrange_.merklemountainrange.md#verify) +* [zip](_src_merklemountainrange_.merklemountainrange.md#protected-zip) * [bigNumberify](_src_merklemountainrange_.merklemountainrange.md#static-bignumberify) * [binarySearch](_src_merklemountainrange_.merklemountainrange.md#static-binarysearch) * [bufferToHex](_src_merklemountainrange_.merklemountainrange.md#static-buffertohex) @@ -139,107 +139,6 @@ ___ ## Methods -### `Protected` _bufferIndexOf - -▸ **_bufferIndexOf**(`array`: Buffer[], `element`: Buffer, `isSorted`: boolean): *number* - -*Inherited from [Base](_src_base_.base.md).[_bufferIndexOf](_src_base_.base.md#protected-_bufferindexof)* - -bufferIndexOf - -**`desc`** Returns the first index of which given buffer is found in array. - -**`example`** -```js -const index = tree.bufferIndexOf(haystack, needle) -``` - -**Parameters:** - -Name | Type | Default | ------- | ------ | ------ | -`array` | Buffer[] | - | -`element` | Buffer | - | -`isSorted` | boolean | false | - -**Returns:** *number* - -- Index number - -___ - -### `Protected` _isHexString - -▸ **_isHexString**(`value`: string): *boolean* - -*Inherited from [Base](_src_base_.base.md).[_isHexString](_src_base_.base.md#protected-_ishexstring)* - -isHexString - -**`desc`** Returns true if value is a hex string. - -**`example`** -```js -console.log(MerkleTree.isHexString('0x1234')) -``` - -**Parameters:** - -Name | Type | ------- | ------ | -`value` | string | - -**Returns:** *boolean* - -___ - -### `Protected` _log2 - -▸ **_log2**(`n`: number): *number* - -*Inherited from [Base](_src_base_.base.md).[_log2](_src_base_.base.md#protected-_log2)* - -log2 - -**`desc`** Returns the log2 of number. - -**Parameters:** - -Name | Type | ------- | ------ | -`n` | number | - -**Returns:** *number* - -___ - -### `Protected` _zip - -▸ **_zip**(`a`: any[], `b`: any[]): *any[][]* - -*Inherited from [Base](_src_base_.base.md).[_zip](_src_base_.base.md#protected-_zip)* - -zip - -**`desc`** Returns true if value is a hex string. - -**`example`** -```js -const zipped = tree.zip(['a', 'b'],['A', 'B']) -console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ] -``` - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`a` | any[] | first array | -`b` | any[] | second array | - -**Returns:** *any[][]* - -___ - ### append ▸ **append**(`data`: Buffer | string): *void* @@ -315,6 +214,35 @@ Name | Type | ___ +### `Protected` bufferIndexOf + +▸ **bufferIndexOf**(`array`: Buffer[], `element`: Buffer, `isSorted`: boolean): *number* + +*Inherited from [Base](_src_base_.base.md).[bufferIndexOf](_src_base_.base.md#protected-bufferindexof)* + +bufferIndexOf + +**`desc`** Returns the first index of which given buffer is found in array. + +**`example`** +```js +const index = tree.bufferIndexOf(haystack, needle) +``` + +**Parameters:** + +Name | Type | Default | +------ | ------ | ------ | +`array` | Buffer[] | - | +`element` | Buffer | - | +`isSorted` | boolean | false | + +**Returns:** *number* + +- Index number + +___ + ### bufferToHex ▸ **bufferToHex**(`value`: Buffer, `withPrefix`: boolean): *string* @@ -573,6 +501,31 @@ Name | Type | ___ +### `Protected` isHexString + +▸ **isHexString**(`value`: string): *boolean* + +*Inherited from [Base](_src_base_.base.md).[isHexString](_src_base_.base.md#static-ishexstring)* + +isHexString + +**`desc`** Returns true if value is a hex string. + +**`example`** +```js +console.log(MerkleTree.isHexString('0x1234')) +``` + +**Parameters:** + +Name | Type | +------ | ------ | +`value` | string | + +**Returns:** *boolean* + +___ + ### isLeaf ▸ **isLeaf**(`index`: number): *boolean* @@ -631,6 +584,26 @@ Name | Type | ___ +### `Protected` log2 + +▸ **log2**(`n`: number): *number* + +*Inherited from [Base](_src_base_.base.md).[log2](_src_base_.base.md#protected-log2)* + +log2 + +**`desc`** Returns the log2 of number. + +**Parameters:** + +Name | Type | +------ | ------ | +`n` | number | + +**Returns:** *number* + +___ + ### mountainHeight ▸ **mountainHeight**(`size`: number): *number* @@ -779,6 +752,33 @@ Name | Type | ___ +### `Protected` zip + +▸ **zip**(`a`: any[], `b`: any[]): *any[][]* + +*Inherited from [Base](_src_base_.base.md).[zip](_src_base_.base.md#protected-zip)* + +zip + +**`desc`** Returns true if value is a hex string. + +**`example`** +```js +const zipped = tree.zip(['a', 'b'],['A', 'B']) +console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ] +``` + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`a` | any[] | first array | +`b` | any[] | second array | + +**Returns:** *any[][]* + +___ + ### `Static` bigNumberify ▸ **bigNumberify**(`value`: any): *BigInt* diff --git a/docs/classes/_src_merkletree_.merkletree.md b/docs/classes/_src_merkletree_.merkletree.md index 720c4b5..dc155f7 100644 --- a/docs/classes/_src_merkletree_.merkletree.md +++ b/docs/classes/_src_merkletree_.merkletree.md @@ -20,15 +20,11 @@ Class reprensenting a Merkle Tree ### Methods -* [_bufferIndexOf](_src_merkletree_.merkletree.md#protected-_bufferindexof) -* [_isHexString](_src_merkletree_.merkletree.md#protected-_ishexstring) -* [_log2](_src_merkletree_.merkletree.md#protected-_log2) -* [_toTreeString](_src_merkletree_.merkletree.md#protected-_totreestring) -* [_zip](_src_merkletree_.merkletree.md#protected-_zip) * [addLeaf](_src_merkletree_.merkletree.md#addleaf) * [addLeaves](_src_merkletree_.merkletree.md#addleaves) * [bigNumberify](_src_merkletree_.merkletree.md#bignumberify) * [binarySearch](_src_merkletree_.merkletree.md#binarysearch) +* [bufferIndexOf](_src_merkletree_.merkletree.md#protected-bufferindexof) * [bufferToHex](_src_merkletree_.merkletree.md#buffertohex) * [bufferify](_src_merkletree_.merkletree.md#bufferify) * [bufferifyFn](_src_merkletree_.merkletree.md#bufferifyfn) @@ -57,14 +53,18 @@ Class reprensenting a Merkle Tree * [getProofs](_src_merkletree_.merkletree.md#getproofs) * [getProofsDFS](_src_merkletree_.merkletree.md#getproofsdfs) * [getRoot](_src_merkletree_.merkletree.md#getroot) +* [isHexString](_src_merkletree_.merkletree.md#protected-ishexstring) * [isUnevenTree](_src_merkletree_.merkletree.md#isuneventree) * [linearSearch](_src_merkletree_.merkletree.md#linearsearch) +* [log2](_src_merkletree_.merkletree.md#protected-log2) * [print](_src_merkletree_.merkletree.md#print) * [resetTree](_src_merkletree_.merkletree.md#resettree) * [toString](_src_merkletree_.merkletree.md#tostring) +* [toTreeString](_src_merkletree_.merkletree.md#protected-totreestring) * [verify](_src_merkletree_.merkletree.md#verify) * [verifyMultiProof](_src_merkletree_.merkletree.md#verifymultiproof) * [verifyMultiProofWithFlags](_src_merkletree_.merkletree.md#verifymultiproofwithflags) +* [zip](_src_merkletree_.merkletree.md#protected-zip) * [bigNumberify](_src_merkletree_.merkletree.md#static-bignumberify) * [binarySearch](_src_merkletree_.merkletree.md#static-binarysearch) * [bufferToHex](_src_merkletree_.merkletree.md#static-buffertohex) @@ -119,124 +119,6 @@ Name | Type | Default | Description | ## Methods -### `Protected` _bufferIndexOf - -▸ **_bufferIndexOf**(`array`: Buffer[], `element`: Buffer, `isSorted`: boolean): *number* - -*Inherited from [Base](_src_base_.base.md).[_bufferIndexOf](_src_base_.base.md#protected-_bufferindexof)* - -bufferIndexOf - -**`desc`** Returns the first index of which given buffer is found in array. - -**`example`** -```js -const index = tree.bufferIndexOf(haystack, needle) -``` - -**Parameters:** - -Name | Type | Default | ------- | ------ | ------ | -`array` | Buffer[] | - | -`element` | Buffer | - | -`isSorted` | boolean | false | - -**Returns:** *number* - -- Index number - -___ - -### `Protected` _isHexString - -▸ **_isHexString**(`value`: string): *boolean* - -*Inherited from [Base](_src_base_.base.md).[_isHexString](_src_base_.base.md#protected-_ishexstring)* - -isHexString - -**`desc`** Returns true if value is a hex string. - -**`example`** -```js -console.log(MerkleTree.isHexString('0x1234')) -``` - -**Parameters:** - -Name | Type | ------- | ------ | -`value` | string | - -**Returns:** *boolean* - -___ - -### `Protected` _log2 - -▸ **_log2**(`n`: number): *number* - -*Inherited from [Base](_src_base_.base.md).[_log2](_src_base_.base.md#protected-_log2)* - -log2 - -**`desc`** Returns the log2 of number. - -**Parameters:** - -Name | Type | ------- | ------ | -`n` | number | - -**Returns:** *number* - -___ - -### `Protected` _toTreeString - -▸ **_toTreeString**(): *string* - -toTreeString - -**`desc`** Returns a visual representation of the merkle tree as a string. - -**`example`** -```js -console.log(tree.toTreeString()) -``` - -**Returns:** *string* - -___ - -### `Protected` _zip - -▸ **_zip**(`a`: any[], `b`: any[]): *any[][]* - -*Inherited from [Base](_src_base_.base.md).[_zip](_src_base_.base.md#protected-_zip)* - -zip - -**`desc`** Returns true if value is a hex string. - -**`example`** -```js -const zipped = tree.zip(['a', 'b'],['A', 'B']) -console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ] -``` - -**Parameters:** - -Name | Type | Description | ------- | ------ | ------ | -`a` | any[] | first array | -`b` | any[] | second array | - -**Returns:** *any[][]* - -___ - ### addLeaf ▸ **addLeaf**(`leaf`: [TLeaf](../modules/_src_merkletree_.md#tleaf), `shouldHash`: boolean): *void* @@ -343,6 +225,35 @@ Name | Type | ___ +### `Protected` bufferIndexOf + +▸ **bufferIndexOf**(`array`: Buffer[], `element`: Buffer, `isSorted`: boolean): *number* + +*Inherited from [Base](_src_base_.base.md).[bufferIndexOf](_src_base_.base.md#protected-bufferindexof)* + +bufferIndexOf + +**`desc`** Returns the first index of which given buffer is found in array. + +**`example`** +```js +const index = tree.bufferIndexOf(haystack, needle) +``` + +**Parameters:** + +Name | Type | Default | +------ | ------ | ------ | +`array` | Buffer[] | - | +`element` | Buffer | - | +`isSorted` | boolean | false | + +**Returns:** *number* + +- Index number + +___ + ### bufferToHex ▸ **bufferToHex**(`value`: Buffer, `withPrefix`: boolean): *string* @@ -972,6 +883,31 @@ const root = tree.getRoot() ___ +### `Protected` isHexString + +▸ **isHexString**(`value`: string): *boolean* + +*Inherited from [Base](_src_base_.base.md).[isHexString](_src_base_.base.md#static-ishexstring)* + +isHexString + +**`desc`** Returns true if value is a hex string. + +**`example`** +```js +console.log(MerkleTree.isHexString('0x1234')) +``` + +**Parameters:** + +Name | Type | +------ | ------ | +`value` | string | + +**Returns:** *boolean* + +___ + ### isUnevenTree ▸ **isUnevenTree**(`treeLayers?`: any[]): *boolean* @@ -1028,6 +964,26 @@ Name | Type | ___ +### `Protected` log2 + +▸ **log2**(`n`: number): *number* + +*Inherited from [Base](_src_base_.base.md).[log2](_src_base_.base.md#protected-log2)* + +log2 + +**`desc`** Returns the log2 of number. + +**Parameters:** + +Name | Type | +------ | ------ | +`n` | number | + +**Returns:** *number* + +___ + ### print ▸ **print**(): *void* @@ -1081,6 +1037,23 @@ console.log(tree.toString()) ___ +### `Protected` toTreeString + +▸ **toTreeString**(): *string* + +toTreeString + +**`desc`** Returns a visual representation of the merkle tree as a string. + +**`example`** +```js +console.log(tree.toTreeString()) +``` + +**Returns:** *string* + +___ + ### verify ▸ **verify**(`proof`: any[], `targetNode`: Buffer | string, `root`: Buffer | string): *boolean* @@ -1160,6 +1133,33 @@ Name | Type | ___ +### `Protected` zip + +▸ **zip**(`a`: any[], `b`: any[]): *any[][]* + +*Inherited from [Base](_src_base_.base.md).[zip](_src_base_.base.md#protected-zip)* + +zip + +**`desc`** Returns true if value is a hex string. + +**`example`** +```js +const zipped = tree.zip(['a', 'b'],['A', 'B']) +console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ] +``` + +**Parameters:** + +Name | Type | Description | +------ | ------ | ------ | +`a` | any[] | first array | +`b` | any[] | second array | + +**Returns:** *any[][]* + +___ + ### `Static` bigNumberify ▸ **bigNumberify**(`value`: any): *BigInt* diff --git a/docs/globals.md b/docs/globals.md index 6905e37..63219fe 100644 --- a/docs/globals.md +++ b/docs/globals.md @@ -7,6 +7,7 @@ ### Modules * ["src/Base"](modules/_src_base_.md) +* ["src/IncrementalMerkleTree"](modules/_src_incrementalmerkletree_.md) * ["src/MerkleMountainRange"](modules/_src_merklemountainrange_.md) * ["src/MerkleTree"](modules/_src_merkletree_.md) * ["src/index"](modules/_src_index_.md) diff --git a/docs/interfaces/_src_incrementalmerkletree_.options.md b/docs/interfaces/_src_incrementalmerkletree_.options.md new file mode 100644 index 0000000..8da2430 --- /dev/null +++ b/docs/interfaces/_src_incrementalmerkletree_.options.md @@ -0,0 +1,33 @@ +[merkletreejs](../README.md) › [Globals](../globals.md) › ["src/IncrementalMerkleTree"](../modules/_src_incrementalmerkletree_.md) › [Options](_src_incrementalmerkletree_.options.md) + +# Interface: Options + +## Hierarchy + +* **Options** + +## Index + +### Properties + +* [arity](_src_incrementalmerkletree_.options.md#optional-arity) +* [depth](_src_incrementalmerkletree_.options.md#optional-depth) +* [zeroValue](_src_incrementalmerkletree_.options.md#optional-zerovalue) + +## Properties + +### `Optional` arity + +• **arity**? : *number* + +___ + +### `Optional` depth + +• **depth**? : *number* + +___ + +### `Optional` zeroValue + +• **zeroValue**? : *any* diff --git a/docs/modules/_src_incrementalmerkletree_.md b/docs/modules/_src_incrementalmerkletree_.md new file mode 100644 index 0000000..c74b79a --- /dev/null +++ b/docs/modules/_src_incrementalmerkletree_.md @@ -0,0 +1,13 @@ +[merkletreejs](../README.md) › [Globals](../globals.md) › ["src/IncrementalMerkleTree"](_src_incrementalmerkletree_.md) + +# Module: "src/IncrementalMerkleTree" + +## Index + +### Classes + +* [IncrementalMerkleTree](../classes/_src_incrementalmerkletree_.incrementalmerkletree.md) + +### Interfaces + +* [Options](../interfaces/_src_incrementalmerkletree_.options.md) diff --git a/docs/modules/_src_index_.md b/docs/modules/_src_index_.md index af18543..ad91663 100644 --- a/docs/modules/_src_index_.md +++ b/docs/modules/_src_index_.md @@ -6,10 +6,17 @@ ### References +* [IncrementalMerkleTree](_src_index_.md#incrementalmerkletree) * [MerkleMountainRange](_src_index_.md#merklemountainrange) ## References +### IncrementalMerkleTree + +• **IncrementalMerkleTree**: + +___ + ### MerkleMountainRange • **MerkleMountainRange**: diff --git a/src/Base.ts b/src/Base.ts index 52af0b5..c470f78 100644 --- a/src/Base.ts +++ b/src/Base.ts @@ -26,7 +26,7 @@ export class Base { *const index = tree.bufferIndexOf(haystack, needle) *``` */ - protected _bufferIndexOf ( + protected bufferIndexOf ( array: Buffer[], element: Buffer, isSorted: boolean = false @@ -308,7 +308,7 @@ export class Base { return v } - if (this._isHexString(v)) { + if (this.isHexString(v)) { return Buffer.from(v.replace('0x', ''), 'hex') } @@ -345,7 +345,7 @@ export class Base { *console.log(MerkleTree.isHexString('0x1234')) *``` */ - protected _isHexString (value: string): boolean { + protected isHexString (value: string): boolean { return Base.isHexString(value) } @@ -355,8 +355,8 @@ export class Base { * @param {Number} value * @return {Number} */ - protected _log2 (n: number): number { - return n === 1 ? 0 : 1 + this._log2((n / 2) | 0) + protected log2 (n: number): number { + return n === 1 ? 0 : 1 + this.log2((n / 2) | 0) } /** @@ -372,7 +372,7 @@ export class Base { *console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ] *``` */ - protected _zip (a: any[], b: any[]): any[][] { + protected zip (a: any[], b: any[]): any[][] { return a.map((e, i) => [e, b[i]]) } diff --git a/src/IncrementalMerkleTree.ts b/src/IncrementalMerkleTree.ts new file mode 100644 index 0000000..b0dd3c1 --- /dev/null +++ b/src/IncrementalMerkleTree.ts @@ -0,0 +1,314 @@ +import Base from './Base' +import treeify from 'treeify' + +export interface Options { + depth?: number + // how many inputs per node + arity?: number + zeroValue?: any +} + +export class IncrementalMerkleTree extends Base { + private depth?: number + private arity?: number + private zeroes?: any[] + private root?: any + private nodes?: any[] + private hashFn: any + private zeroValue: any + + constructor (hashFn: any, options: Options) { + super() + this.hashFn = hashFn + if (options.depth) { + this.depth = options.depth + } + if (options.arity) { + this.arity = options.arity + } + + if (this.depth < 1) { + throw new Error('depth must be greater than 0') + } + + if (this.arity < 1) { + throw new Error('arity must be greater than 0') + } + + const nodes = [] + let zeroValue = options.zeroValue + this.zeroValue = zeroValue + this.zeroes = [] + if (this.depth) { + for (let i = 0; i < this.depth; i++) { + this.zeroes.push(zeroValue) + nodes[i] = [] + zeroValue = this.hashFn(Array(this.arity).fill(zeroValue)) + } + } + + this.nodes = nodes + this.root = zeroValue + } + + getRoot () { + return this.root + } + + getHexRoot () { + return this.bufferToHex(this.bufferify(this.getRoot())) + } + + insert (leaf: any) { + if (this.depth && this.arity) { + if (this.nodes[0].length >= this.getMaxLeaves()) { + throw new Error('tree is full') + } + } + + let node = leaf + let index = this.nodes[0].length + + for (let level = 0; level < this.depth; level += 1) { + const position = index % this.arity + const levelStartIndex = index - position + const levelEndIndex = levelStartIndex + this.arity + + const children = [] + this.nodes[level][index] = node + + for (let i = levelStartIndex; i < levelEndIndex; i += 1) { + if (i < this.nodes[level].length) { + children.push(this.nodes[level][i]) + } else { + children.push(this.zeroes[level]) + } + } + + node = this.hashFn(children) + index = Math.floor(index / this.arity) + } + + this.root = node + } + + delete (index: number) { + this.update(index, this.zeroValue) + } + + update (index: number, newLeaf: any) { + if (index < 0 || index >= this.nodes[0].length) { + throw new Error('out of bounds') + } + + let node = newLeaf + + for (let level = 0; level < this.depth; level += 1) { + const position = index % this.arity + const levelStartIndex = index - position + const levelEndIndex = levelStartIndex + this.arity + + const children = [] + this.nodes[level][index] = node + + for (let i = levelStartIndex; i < levelEndIndex; i += 1) { + if (i < this.nodes[level].length) { + children.push(this.nodes[level][i]) + } else { + children.push(this.zeroes[level]) + } + } + + node = this.hashFn(children) + index = Math.floor(index / this.arity) + } + + this.root = node + } + + getDepth (): number { + return this.depth + } + + getArity (): number { + return this.arity + } + + getMaxLeaves (): number { + return this.depth ** this.arity + } + + indexOf (leaf: any): number { + return this.nodes[0].indexOf(leaf) + } + + getLeaves () { + const leaves = this.copyList(this.nodes[0]) + const index = this.nodes[0].length + for (let i = index; i < this.getMaxLeaves(); i++) { + leaves[i] = this.zeroValue + } + return leaves + } + + copyList (list: any[]) { + return list.map((x: any) => BigInt(x)) + } + + getLayers ():any[] { + const layers = [] + + for (const list of this.nodes) { + layers.push(this.copyList(list)) + } + + if (layers[0].length < this.getMaxLeaves()) { + let index = layers[0].length + for (let i = index; i < this.getMaxLeaves(); i++) { + layers[0][i] = this.zeroValue + } + for (let level = 0; level < this.depth; level++) { + const position = index % this.arity + const levelStartIndex = index - position + const levelEndIndex = levelStartIndex + this.arity + + for (let i = levelStartIndex; i < levelEndIndex; i++) { + if (i >= layers[level].length) { + layers[level][i] = this.zeroes[level] + } + } + index = Math.floor(index / this.arity) + } + } + + layers.push([this.root]) + return layers + } + + getHexLayers ():string[] { + return this.getLayers().reduce((acc: string[][], item: Buffer[]) => { + if (Array.isArray(item)) { + acc.push(item.map(layer => this.bufferToHex(this.bufferify(layer)))) + } else { + acc.push(item) + } + + return acc + }, []) + } + + getLayersAsObject ():any { + const layers: any[] = this.getLayers().map((layer: any) => layer.map((value: any) => this.bufferToHex(this.bufferify(value), false))) + const objs = [] + for (let i = 0; i < layers.length; 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] + } + + computeRoot () { + let node : any + let index = this.nodes[0].length + for (let level = 0; level < this.depth; level += 1) { + const position = index % this.arity + const levelStartIndex = index - position + const levelEndIndex = levelStartIndex + this.arity + + const children = [] + + for (let i = levelStartIndex; i < levelEndIndex; i += 1) { + if (i < this.nodes[level].length) { + children.push(this.nodes[level][i]) + } else { + children.push(this.zeroes[level]) + } + } + + node = this.hashFn(children) + index = Math.floor(index / this.arity) + } + + return node + } + + getProof (index: number): any { + if (index < 0 || index >= this.nodes[0].length) { + throw new Error('The leaf does not exist in this tree') + } + + const siblings: Node[][] = [] + const pathIndices: number[] = [] + const leafIndex = index + + for (let level = 0; level < this.depth; level += 1) { + const position = index % this.arity + const levelStartIndex = index - position + const levelEndIndex = levelStartIndex + this.arity + + pathIndices[level] = position + siblings[level] = [] + + for (let i = levelStartIndex; i < levelEndIndex; i += 1) { + if (i !== index) { + if (i < this.nodes[level].length) { + siblings[level].push(this.nodes[level][i]) + } else { + siblings[level].push(this.zeroes[level]) + } + } + } + + index = Math.floor(index / this.arity) + } + + return { root: this.root, leaf: this.nodes[0][leafIndex], pathIndices, siblings } + } + + verify (proof: any): boolean { + let node = proof.leaf + + for (let i = 0; i < proof.siblings.length; i += 1) { + const children = proof.siblings[i].slice() + + children.splice(proof.pathIndices[i], 0, node) + + node = this.hashFn(children) + } + + return proof.root === node + } + + toString ():string { + return this.toTreeString() + } + + protected toTreeString ():string { + const obj = this.getLayersAsObject() + return treeify.asTree(obj, true) + } +} + +if (typeof window !== 'undefined') { + ;(window as any).IncrementalMerkleTree = IncrementalMerkleTree +} + +export default IncrementalMerkleTree diff --git a/src/MerkleMountainRange.ts b/src/MerkleMountainRange.ts index 16620cd..ae7e874 100644 --- a/src/MerkleMountainRange.ts +++ b/src/MerkleMountainRange.ts @@ -305,7 +305,7 @@ export class MerkleMountainRange extends Base { // find the mountain where the target index belongs to let cursor = 0 - let targetPeak + let targetPeak : number const peakIndexes = this.getPeakIndexes(width) for (let i = 0; i < peakIndexes.length; i++) { @@ -341,7 +341,7 @@ export class MerkleMountainRange extends Base { } // calculate the summit hash climbing up again - let node + let node : any while (height < path.length) { // move cursor cursor = path[height] @@ -489,4 +489,8 @@ export class MerkleMountainRange extends Base { } } +if (typeof window !== 'undefined') { + ;(window as any).MerkleMountainRange = MerkleMountainRange +} + export default MerkleMountainRange diff --git a/src/MerkleTree.ts b/src/MerkleTree.ts index 05a53de..3581b07 100644 --- a/src/MerkleTree.ts +++ b/src/MerkleTree.ts @@ -5,7 +5,7 @@ import treeify from 'treeify' import Base from './Base' // TODO: Clean up and DRY up code -// Disclaimer: The multiproof code is unaudited and may possibly contain serious issues. It's in a hacky state as is and it's begging for a rewrite! +// Disclaimer: The multiproof code is unaudited and may possibly contain serious issues. It's in a hacky state as is and needs to be rewritten. type TValue = Buffer | BigInt | string | number | null | undefined type THashFnResult = Buffer | string @@ -150,11 +150,11 @@ export class MerkleTree extends Base { } } - this.layers = [this.leaves] - this._createHashes(this.leaves) + this.createHashes(this.leaves) } - private _createHashes (nodes: any[]) { + private createHashes (nodes: any[]) { + this.layers = [nodes] while (nodes.length > 1) { const layerIndex = this.layers.length @@ -273,7 +273,7 @@ export class MerkleTree extends Base { } } - return this.leaves.filter(leaf => this._bufferIndexOf(values, leaf, this.sortLeaves) !== -1) + return this.leaves.filter(leaf => this.bufferIndexOf(values, leaf, this.sortLeaves) !== -1) } return this.leaves @@ -920,7 +920,7 @@ export class MerkleTree extends Base { els = els.sort(Buffer.compare) } - let ids = els.map((el) => this._bufferIndexOf(this.leaves, el, this.sortLeaves)).sort((a, b) => a === b ? 0 : a > b ? 1 : -1) + let ids = els.map((el) => this.bufferIndexOf(this.leaves, el, this.sortLeaves)).sort((a, b) => a === b ? 0 : a > b ? 1 : -1) if (!ids.every((idx) => idx !== -1)) { throw new Error('Element does not exist in Merkle tree') } @@ -933,7 +933,7 @@ export class MerkleTree extends Base { const layer = this.layers[i] for (let j = 0; j < ids.length; j++) { const idx = ids[j] - const pairElement = this._getPairNode(layer, idx) + const pairElement = this.getPairNode(layer, idx) hashes.push(layer[idx]) if (pairElement) { @@ -950,7 +950,7 @@ export class MerkleTree extends Base { return proof.filter((value) => !hashes.includes(value)) } - return this.getProofIndices(indices, this._log2((tree.length / 2) | 0)).map(index => tree[index]) + return this.getProofIndices(indices, Math.log2((tree.length / 2) | 0)).map(index => tree[index]) } private getMultiProofForUnevenTree (tree?: any[], indices?: any[]):Buffer[] { @@ -1043,7 +1043,7 @@ export class MerkleTree extends Base { if (leaves.every(Number.isInteger)) { ids = [...leaves].sort((a, b) => a === b ? 0 : a > b ? 1 : -1) // Indices where passed } else { - ids = leaves.map((el) => this._bufferIndexOf(this.leaves, el, this.sortLeaves)).sort((a, b) => a === b ? 0 : a > b ? 1 : -1) + ids = leaves.map((el) => this.bufferIndexOf(this.leaves, el, this.sortLeaves)).sort((a, b) => a === b ? 0 : a > b ? 1 : -1) } if (!ids.every((idx: number) => idx !== -1)) { @@ -1059,7 +1059,7 @@ export class MerkleTree extends Base { ids = ids.reduce((ids, idx) => { const skipped = tested.includes(layer[idx]) if (!skipped) { - const pairElement = this._getPairNode(layer, idx) + const pairElement = this.getPairNode(layer, idx) const proofUsed = _proofs.includes(layer[idx]) || _proofs.includes(pairElement) pairElement && flags.push(!proofUsed) tested.push(layer[idx]) @@ -1186,10 +1186,10 @@ export class MerkleTree extends Base { proof = (proof as any[]).map(leaf => this.bufferify(leaf)) const tree = {} - for (const [index, leaf] of this._zip(proofIndices, proofLeaves)) { + for (const [index, leaf] of this.zip(proofIndices, proofLeaves)) { tree[(2 ** depth) + index] = leaf } - for (const [index, proofitem] of this._zip(this.getProofIndices(proofIndices, depth), proof)) { + for (const [index, proofitem] of this.zip(this.getProofIndices(proofIndices, depth), proof)) { tree[index] = proofitem } let indexqueue = Object.keys(tree).map(value => +value).sort((a, b) => a - b) @@ -1360,7 +1360,7 @@ export class MerkleTree extends Base { *const node = tree.getPairNode(layer, index) *``` */ - private _getPairNode (layer: Buffer[], idx: number):Buffer { + private getPairNode (layer: Buffer[], idx: number):Buffer { const pairIdx = idx % 2 === 0 ? idx + 1 : idx - 1 if (pairIdx < layer.length) { @@ -1379,7 +1379,7 @@ export class MerkleTree extends Base { *console.log(tree.toTreeString()) *``` */ - protected _toTreeString ():string { + protected toTreeString ():string { const obj = this.getLayersAsObject() return treeify.asTree(obj, true) } @@ -1393,7 +1393,7 @@ export class MerkleTree extends Base { *``` */ toString ():string { - return this._toTreeString() + return this.toTreeString() } isUnevenTree (treeLayers?: any[]) { @@ -1406,7 +1406,7 @@ export class MerkleTree extends Base { } private calculateRootForUnevenTree (leafIndices: number[], leafHashes: any[], totalLeavesCount: number, proofHashes: any[]) { - const leafTuples = this._zip(leafIndices, leafHashes).sort(([indexA], [indexB]) => indexA - indexB) + const leafTuples = this.zip(leafIndices, leafHashes).sort(([indexA], [indexB]) => indexA - indexB) const leafTupleIndices = leafTuples.map(([index]) => index) const proofIndices = this.getProofIndicesForUnevenTree(leafTupleIndices, totalLeavesCount) @@ -1416,7 +1416,7 @@ export class MerkleTree extends Base { const indices = proofIndices[i] const sliceStart = nextSliceStart nextSliceStart += indices.length - proofTuplesByLayers[i] = this._zip(indices, proofHashes.slice(sliceStart, nextSliceStart)) + proofTuplesByLayers[i] = this.zip(indices, proofHashes.slice(sliceStart, nextSliceStart)) } const tree = [leafTuples] diff --git a/src/index.ts b/src/index.ts index a404376..d58c21f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import MerkleTree from './MerkleTree' export { MerkleTree } export { MerkleMountainRange } from './MerkleMountainRange' +export { IncrementalMerkleTree } from './IncrementalMerkleTree' export default MerkleTree diff --git a/test/IncrementalMerkleTree.test.js b/test/IncrementalMerkleTree.test.js new file mode 100644 index 0000000..72fe74f --- /dev/null +++ b/test/IncrementalMerkleTree.test.js @@ -0,0 +1,205 @@ +const test = require('tape') +const { IncrementalMerkleTree } = require('../') +const { buildPoseidon } = require('circomlibjs') + +test('poseidon - 2 depth 2 arity', async t => { + t.plan(18) + + const _poseidon = await buildPoseidon() + const poseidon = (inputs) => { + const hash = _poseidon(inputs.map(IncrementalMerkleTree.bigNumberify)) + const bn = IncrementalMerkleTree.bigNumberify(_poseidon.F.toString(hash)) + return bn + } + const tree = new IncrementalMerkleTree(poseidon, { + depth: 2, + arity: 2, + zeroValue: BigInt(0) + }) + + t.equal(tree.getDepth(), 2) + t.equal(tree.getArity(), 2) + t.equal(tree.getMaxLeaves(), 4) + t.equal(tree.getHexRoot(), '0x1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1') + tree.insert(poseidon([BigInt(1)])) + t.equal(tree.getHexRoot(), '0x19da8f56a40748020782233bc94fd32e81ba9bb9884e8848310878aa205b0497') + + t.deepEqual(tree.getLayers(), + [ + [ + 18586133768512220936620570745912940619677854269274689475585506675881198879027n, + 0n, + 0n, + 0n + ], + [ + 5094515486147324152810396339294786824839256615363920109013032939290670981070n, + 14744269619966411208579211824598458697587494354926760081771325075741142829156n + ], + [ + 11693983160260563097655073427752835408055631740894534147682792587184540681367n + ] + ] + ) + + tree.insert(poseidon([BigInt(2)])) + t.equal(tree.getHexRoot(), '0x2c2ea920619c56f8dba3b19e5c3d76d346527e772ef6d90ef0dd323631ef1cb6') + t.equal(tree.indexOf(poseidon([BigInt(2)])), 1) + tree.delete(1) + t.equal(tree.getHexRoot(), '0x19da8f56a40748020782233bc94fd32e81ba9bb9884e8848310878aa205b0497') + tree.update(0, poseidon([BigInt(3)])) + t.equal(tree.getHexRoot(), '0x2e94d2b74c9ea1cafeb6da975a5475e7d4c848e6cb3346484b146d5cfaec87eb') + tree.update(1, poseidon([BigInt(2)])) + t.deepEqual(tree.getLeaves(), + [ + 6018413527099068561047958932369318610297162528491556075919075208700178480084n, + 8645981980787649023086883978738420856660271013038108762834452721572614684349n, + 0n, + 0n + ] + ) + t.deepEqual(tree.getLayers(), + [ + [ + 6018413527099068561047958932369318610297162528491556075919075208700178480084n, + 8645981980787649023086883978738420856660271013038108762834452721572614684349n, + 0n, + 0n + ], + [ + 14909064445584595308539830952419236478129618691160103903001052664000042483490n, + 14744269619966411208579211824598458697587494354926760081771325075741142829156n + ], + [ + 3510912107467918110461523845783070983430853155915191132694596979334180474120n + ] + ]) + tree.insert(poseidon([BigInt(3)])) + t.deepEqual(tree.getLayers(), + [ + [ + 6018413527099068561047958932369318610297162528491556075919075208700178480084n, + 8645981980787649023086883978738420856660271013038108762834452721572614684349n, + 6018413527099068561047958932369318610297162528491556075919075208700178480084n, + 0n + ], + [ + 14909064445584595308539830952419236478129618691160103903001052664000042483490n, + 21239441820082410876343833288590122027808783310040459823992255344659808702818n + ], + [ + 13634681385965571274925238151540568262573782028276541919047631799812805578309n + ] + ]) + + tree.insert(poseidon([BigInt(4)])) + t.deepEqual(tree.getLayers(), + [ + [ + 6018413527099068561047958932369318610297162528491556075919075208700178480084n, + 8645981980787649023086883978738420856660271013038108762834452721572614684349n, + 6018413527099068561047958932369318610297162528491556075919075208700178480084n, + 9900412353875306532763997210486973311966982345069434572804920993370933366268n + ], + [ + 14909064445584595308539830952419236478129618691160103903001052664000042483490n, + 15866811995824089293749468808478915337040145970836273016636380754543464442080n + ], + [ + 16131156821127350901643174230980638228438939582106721266118265564186720968083n + ] + ]) + t.equal(tree.getRoot(), 16131156821127350901643174230980638228438939582106721266118265564186720968083n) + t.equal(tree.getHexRoot(), '0x23a9e944fc7d734b6ef70b73c9ecc9ae97051b709c29f41339b039b701e99d93') + t.deepEqual(tree.getLayersAsObject(), + { + '23a9e944fc7d734b6ef70b73c9ecc9ae97051b709c29f41339b039b701e99d93': { + '20f63b3f6cf280e6a4a488a825d7fdc948ee0a02a2714a532f6d0e747bbfbf22': { + d4e4d24b890fe6799be4cf57ad13078ec0fbaa9fe91423ba8bbd0c2d7043bd: null, + '131d73cf6b30079aca0dff6a561cd0ee50b540879abe379a25a06b24bde2bebd': null + }, + '23144c1e7794f62515c2ccbaee3076d2e40b673fcba5da8a6457387e054068e0': { + d4e4d24b890fe6799be4cf57ad13078ec0fbaa9fe91423ba8bbd0c2d7043bd: null, + '15e36f4ff92e2211fa8ed9f7af707f6c8c0f1442252a85150d2b8d2038890dfc': null + } + } + } + ) + t.equal(tree.toString(), +`└─ 23a9e944fc7d734b6ef70b73c9ecc9ae97051b709c29f41339b039b701e99d93 + ├─ 20f63b3f6cf280e6a4a488a825d7fdc948ee0a02a2714a532f6d0e747bbfbf22 + │ ├─ d4e4d24b890fe6799be4cf57ad13078ec0fbaa9fe91423ba8bbd0c2d7043bd + │ └─ 131d73cf6b30079aca0dff6a561cd0ee50b540879abe379a25a06b24bde2bebd + └─ 23144c1e7794f62515c2ccbaee3076d2e40b673fcba5da8a6457387e054068e0 + ├─ d4e4d24b890fe6799be4cf57ad13078ec0fbaa9fe91423ba8bbd0c2d7043bd + └─ 15e36f4ff92e2211fa8ed9f7af707f6c8c0f1442252a85150d2b8d2038890dfc +`) +}) + +test('poseidon - 2 depth 3 arity', async t => { + t.plan(3) + + const _poseidon = await buildPoseidon() + const poseidon = (inputs) => { + const hash = _poseidon(inputs.map(IncrementalMerkleTree.bigNumberify)) + const bn = IncrementalMerkleTree.bigNumberify(_poseidon.F.toString(hash)) + return bn + } + const tree = new IncrementalMerkleTree(poseidon, { + depth: 2, + arity: 3, + zeroValue: BigInt(0) + }) + + t.equal(tree.getDepth(), 2) + t.equal(tree.getArity(), 3) + tree.insert(poseidon([BigInt(1)])) + tree.insert(poseidon([BigInt(2)])) + tree.insert(poseidon([BigInt(3)])) + tree.insert(poseidon([BigInt(4)])) + tree.insert(poseidon([BigInt(5)])) + tree.insert(poseidon([BigInt(6)])) + tree.insert(poseidon([BigInt(7)])) + tree.insert(poseidon([BigInt(8)])) + t.equal(tree.getHexRoot(), '0xe38e8da4dd7c981fb9e47ca06a88447d3111a3796e2ed8ecc0c80c341b945a') +}) + +test('poseidon - proof', async t => { + t.plan(4) + + const _poseidon = await buildPoseidon() + const poseidon = (inputs) => { + const hash = _poseidon(inputs.map(IncrementalMerkleTree.bigNumberify)) + const bn = IncrementalMerkleTree.bigNumberify(_poseidon.F.toString(hash)) + return bn + } + const tree = new IncrementalMerkleTree(poseidon, { + depth: 2, + arity: 2, + zeroValue: BigInt(0) + }) + + t.equal(tree.getDepth(), 2) + t.equal(tree.getArity(), 2) + tree.insert(poseidon([BigInt(1)])) + tree.insert(poseidon([BigInt(2)])) + tree.insert(poseidon([BigInt(3)])) + tree.insert(poseidon([BigInt(4)])) + const proof = tree.getProof(2) + t.deepEqual(proof, + { + root: 4924824719679653695544344112002466960362482050425504983922056625160325123496n, + leaf: 6018413527099068561047958932369318610297162528491556075919075208700178480084n, + pathIndices: [0, 1], + siblings: [ + [ + 9900412353875306532763997210486973311966982345069434572804920993370933366268n + ], + [ + 10058687713083746196667355667918512760470030038024584531967182749893253193558n + ] + ] + } + ) + t.true(tree.verify(proof)) +}) diff --git a/test/MerkleTree.test.js b/test/MerkleTree.test.js index 6c620e6..6bee853 100644 --- a/test/MerkleTree.test.js +++ b/test/MerkleTree.test.js @@ -1327,10 +1327,10 @@ test.skip('1M leaves keccak256', t => { test('poseidon hash', async t => { t.plan(4) - const _poseidon = await buildPoseidon() + const poseidon = await buildPoseidon() const poseidonHash = (inputs) => { - const hash = _poseidon(inputs.map(MerkleTree.bigNumberify)) - const bn = MerkleTree.bigNumberify(_poseidon.F.toString(hash)) + const hash = poseidon(inputs.map(MerkleTree.bigNumberify)) + const bn = MerkleTree.bigNumberify(poseidon.F.toString(hash)) return MerkleTree.bufferify(bn) }