Add marshal tree method

This commit is contained in:
Miguel Mota 2022-11-10 22:15:00 -08:00
parent 68566c5ae9
commit 5c169b13d4
No known key found for this signature in database
GPG Key ID: 67EC1161588A00F9
3 changed files with 209 additions and 0 deletions

View File

@ -48,6 +48,7 @@ Class reprensenting a Merkle Tree
* [getLeafIndex](_src_merkletree_.merkletree.md#getleafindex)
* [getLeaves](_src_merkletree_.merkletree.md#getleaves)
* [getMultiProof](_src_merkletree_.merkletree.md#getmultiproof)
* [getOptions](_src_merkletree_.merkletree.md#getoptions)
* [getPositionalHexProof](_src_merkletree_.merkletree.md#getpositionalhexproof)
* [getProof](_src_merkletree_.merkletree.md#getproof)
* [getProofFlags](_src_merkletree_.merkletree.md#getproofflags)
@ -71,9 +72,11 @@ Class reprensenting a Merkle Tree
* [linearSearch](_src_merkletree_.merkletree.md#static-linearsearch)
* [marshalLeaves](_src_merkletree_.merkletree.md#static-marshalleaves)
* [marshalProof](_src_merkletree_.merkletree.md#static-marshalproof)
* [marshalTree](_src_merkletree_.merkletree.md#static-marshaltree)
* [print](_src_merkletree_.merkletree.md#static-print)
* [unmarshalLeaves](_src_merkletree_.merkletree.md#static-unmarshalleaves)
* [unmarshalProof](_src_merkletree_.merkletree.md#static-unmarshalproof)
* [unmarshalTree](_src_merkletree_.merkletree.md#static-unmarshaltree)
* [verify](_src_merkletree_.merkletree.md#static-verify)
## Constructors
@ -736,6 +739,30 @@ Name | Type | Description |
___
### getOptions
**getOptions**(): *object*
**Returns:** *object*
* **complete**: *boolean* = this.complete
* **duplicateOdd**: *boolean* = this.duplicateOdd
* **fillDefaultHash**: *string* = this.fillDefaultHash?.toString() ?? null
* **hashLeaves**: *boolean* = this.hashLeaves
* **isBitcoinTree**: *boolean* = this.isBitcoinTree
* **sort**: *boolean* = this.sort
* **sortLeaves**: *boolean* = this.sortLeaves
* **sortPairs**: *boolean* = this.sortPairs
___
### getPositionalHexProof
**getPositionalHexProof**(`leaf`: Buffer | string, `index?`: number): *(string | number)[][]*
@ -1352,6 +1379,20 @@ Name | Type | Description |
___
### `Static` marshalTree
**marshalTree**(`tree`: [MerkleTree](_src_merkletree_.merkletree.md)): *string*
**Parameters:**
Name | Type |
------ | ------ |
`tree` | [MerkleTree](_src_merkletree_.merkletree.md) |
**Returns:** *string*
___
### `Static` print
**print**(`tree`: any): *void*
@ -1427,6 +1468,22 @@ Name | Type |
___
### `Static` unmarshalTree
**unmarshalTree**(`jsonStr`: string | object, `hashFn`: any, `options`: [Options](../interfaces/_src_merkletree_.options.md)): *[MerkleTree](_src_merkletree_.merkletree.md)*
**Parameters:**
Name | Type | Default |
------ | ------ | ------ |
`jsonStr` | string | object | - |
`hashFn` | any | SHA256 |
`options` | [Options](../interfaces/_src_merkletree_.options.md) | {} |
**Returns:** *[MerkleTree](_src_merkletree_.merkletree.md)*
___
### `Static` verify
**verify**(`proof`: any[], `targetNode`: Buffer | string, `root`: Buffer | string, `hashFn`: any, `options`: [Options](../interfaces/_src_merkletree_.options.md)): *boolean*

View File

@ -112,6 +112,19 @@ export class MerkleTree extends Base {
this.processLeaves(leaves)
}
public getOptions() {
return {
complete: this.complete,
isBitcoinTree: this.isBitcoinTree,
hashLeaves: this.hashLeaves,
sortLeaves: this.sortLeaves,
sortPairs: this.sortPairs,
sort: this.sort,
fillDefaultHash: this.fillDefaultHash?.toString() ?? null,
duplicateOdd: this.duplicateOdd
}
}
private processLeaves (leaves: TLeaf[]) {
if (this.hashLeaves) {
leaves = leaves.map(this.hashFn)
@ -740,6 +753,39 @@ export class MerkleTree extends Base {
})
}
static marshalTree (tree: MerkleTree):string {
const root = tree.getHexRoot()
const leaves = tree.leaves.map(leaf => MerkleTree.bufferToHex(leaf))
const layers = tree.getHexLayers()
const options = tree.getOptions()
return JSON.stringify({
options,
root,
layers,
leaves,
}, null, 2)
}
static unmarshalTree (jsonStr: string | object, hashFn = SHA256, options: Options = {}):MerkleTree {
let parsed :any = null
if (typeof jsonStr === 'string') {
parsed = JSON.parse(jsonStr)
} else if (jsonStr instanceof Object) {
parsed = jsonStr
} else {
throw new Error('Expected type of string or object')
}
if (!parsed) {
throw new Error('could not parse json')
}
options = Object.assign({}, parsed.options || {}, options)
return new MerkleTree(parsed.leaves, hashFn, options)
}
/**
* getProofIndices
* @desc Returns the proof indices for given tree indices.

View File

@ -1046,6 +1046,112 @@ test('unmarshal proof', t => {
t.equal(proof[1].data.toString('hex'), '43e061172b1177f25d0f156b2d2ed11728006fade8e167ff3d1b9dbc979a3358')
})
test('marshal tree', t => {
t.plan(7)
const leaves = ['a', 'b', 'c'].map(keccak256)
const tree = new MerkleTree(leaves, keccak256, { sort: true })
const jsonTree = MerkleTree.marshalTree(tree)
t.equal(typeof jsonTree, 'string')
const parsed = JSON.parse(jsonTree)
t.deepEqual(parsed.options, {
complete: false,
isBitcoinTree: false,
hashLeaves: false,
sortLeaves: true,
sortPairs: true,
sort: true,
fillDefaultHash: null,
duplicateOdd: false
})
t.equal(parsed.root, '0xc3b537cc8a2c6dcb3657718e1f3505ff751ff8c2eba2a70460df2cbee2b1413a')
t.equal(parsed.leaves.length, 3)
t.deepEqual(parsed.leaves, [
'0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2',
'0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb',
'0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510'
])
t.equal(parsed.layers.length, 3)
t.deepEqual(parsed.layers, [
[
'0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2',
'0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb',
'0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510'
],
[
'0x7dea550f679f3caab547cbbc5ee1a4c978c8c039b572ba00af1baa6481b88360',
'0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510'
],
[
'0xc3b537cc8a2c6dcb3657718e1f3505ff751ff8c2eba2a70460df2cbee2b1413a'
]
])
})
test('unmarshal tree', t => {
t.plan(4)
const json = `{
"options": {
"complete": false,
"isBitcoinTree": false,
"hashLeaves": false,
"sortLeaves": true,
"sortPairs": true,
"sort": true,
"fillDefaultHash": null,
"duplicateOdd": false
},
"root": "0xc3b537cc8a2c6dcb3657718e1f3505ff751ff8c2eba2a70460df2cbee2b1413a",
"layers": [
[
"0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2",
"0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb",
"0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510"
],
[
"0x7dea550f679f3caab547cbbc5ee1a4c978c8c039b572ba00af1baa6481b88360",
"0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510"
],
[
"0xc3b537cc8a2c6dcb3657718e1f3505ff751ff8c2eba2a70460df2cbee2b1413a"
]
],
"leaves": [
"0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2",
"0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb",
"0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510"
]
}`
const parsed = JSON.parse(json)
const tree = MerkleTree.unmarshalTree(json, keccak256)
t.equal(tree.getHexRoot(), parsed.root)
t.deepEqual(tree.getOptions(), parsed.options)
t.deepEqual(tree.getHexLeaves(), parsed.leaves)
t.deepEqual(tree.getHexLayers(), parsed.layers)
})
test('getOptions', t => {
t.plan(1)
const leaves = ['a', 'b', 'c', 'd', 'e', 'f'].map(sha256)
const tree = new MerkleTree(leaves, sha256, { sortPairs: true, sortLeaves: true })
t.deepEqual(tree.getOptions(), {
complete: false,
isBitcoinTree: false,
hashLeaves: false,
sortLeaves: true,
sortPairs: true,
sort: false,
fillDefaultHash: null,
duplicateOdd: false
})
})
test('fillDefaultHashes', t => {
t.plan(1)