diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..773017e --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,25 @@ +name: Test typescript package + +on: + pull_request: + types: ["opened", "edited", "reopened", "synchronize"] + branches: [master] + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Set up Node 14.7.x + uses: actions/setup-node@v2 + with: + go-version: 14.7.0 + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Get dependencies + run: yarn install + + - name: Run typescript tests + run: yarn test-ts diff --git a/js/src/.gitkeep b/js/src/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/js/src/ts/append-proofs.ts b/js/src/ts/append-proofs.ts new file mode 100644 index 0000000..d403f74 --- /dev/null +++ b/js/src/ts/append-proofs.ts @@ -0,0 +1,174 @@ +import { defaultProofOptions, defaultTreeOptions, getNewRootParams, getRootParams, proof, proofOptions, treeOptions } from './common' +import { bitCount32, to32ByteBuffer, from32ByteBuffer } from './utils' + +// This is the SingleProof.generate algorithm, using the elementCount as index, +// thereby generating a subset of those same decommitments, but only those +// "to the left" of the index, since all nodes "to the right" are non-existent. +// Also, the left sub-tree's root (always defined as i=2 in the tree), is always +// required, as every single append is "to the right" of it, by definition. +export const generate = (tree: Array, elementCount: number, options: proofOptions = defaultProofOptions): proof => { + const decommitments = Array() + const leafCount = tree.length >>> 1 + + for (let i = leafCount + elementCount; i > 1; i >>>= 1) { + if (i & 1 || i === 2) { + decommitments.unshift(tree[i - 1]) + } + } + + if (options.compact) + return { + elementCount: 0, + compactProof: [to32ByteBuffer(elementCount)].concat(decommitments.map(Buffer.from)), + decommitments: Array() + } + + return { + elementCount, + decommitments: decommitments.map(Buffer.from), + compactProof: Array() + } +} + +// This is the SingleProof.getRoot algorithm, where the amount of decommitments, +// must equal the amount of bits in the elementCount, and we are recovering the +// root that can be built from the decommitments, hashed from "right" to "left". +// Note, it is implied that there is nothing to the right of the "right-most" +// decommitment, explaining the departure from the SingleProof.getRoot algorithm. +export const getRoot = (params: getRootParams, options: treeOptions = defaultTreeOptions): { root: Buffer, elementCount: number } => { + if (params.compactProof.length > 0) { + params.elementCount = from32ByteBuffer(params.compactProof[0]) + params.decommitments = params.compactProof.slice(1) + } + + let index = bitCount32(params.elementCount) + let hash = params.decommitments[--index] + + while (index > 0) { + hash = options.hashFunction(params.decommitments[--index], hash) + } + + return { root: hash, elementCount: params.elementCount } +} + +// This is identical to the above getRoot algorithm, differing only in that the +// new root (due to the appended leaf), is computed along the way. +// Note, it is implied that there is nothing to the right of the leaf being +// appended, explaining the departure from the SingleProof.getNewRoot algorithm. +// See getRoot for relevant inline comments. +const getNewRootSingle = (appendLeaf: Buffer, compactProof: Array, elementCount: number, decommitments: Array, options: treeOptions = defaultTreeOptions): { root: Buffer, newRoot: Buffer, elementCount: number } => { + if (compactProof.length > 0) { + elementCount = from32ByteBuffer(compactProof[0]) + decommitments = compactProof.slice(1) + } + + let index = bitCount32(elementCount) + let hash = decommitments[--index] + let appendHash = options.hashFunction(decommitments[index], appendLeaf) + + while (index > 0) { + appendHash = options.hashFunction(decommitments[--index], appendHash) + hash = options.hashFunction(decommitments[index], hash) + } + + return { root: hash, newRoot: appendHash, elementCount } +} + +// If appendHashes[0]'s level-localized index is odd, merge with decommitment at this level. If more +// elements are appended than existed in the tree, appendHashes[0]'s level-localized index will tend +// to 0, and no longer be merged with decommitments. If appendHashes[0]'s level-localized index is +// even, hash with node to the right. An odd level-localized index is either at appendHashes[0] or +// index == upperBound. If upperBound == 0, we got to the new root. +const getNewRootMulti = (appendLeafs: Array, compactProof: Array, elementCount: number, decommitments: Array, options: treeOptions = defaultTreeOptions): { root: Buffer, newRoot: Buffer, elementCount: number } => { + if (compactProof.length > 0) { + elementCount = from32ByteBuffer(compactProof[0]) + decommitments = compactProof.slice(1) + } + + let decommitmentIndex = bitCount32(elementCount) - 1 + let hash = decommitments[decommitmentIndex] + let appendHashes = appendLeafs.map((leaf) => leaf) + let upperBound = elementCount + appendLeafs.length - 1 + let writeIndex = 0 + let readIndex = 0 + let offset = elementCount + let index = offset + + while (upperBound > 0) { + if (writeIndex === 0 && index & 1) { + appendHashes[writeIndex++] = options.hashFunction(decommitments[decommitmentIndex--], appendHashes[readIndex++]) + + if (decommitmentIndex >= 0) hash = options.hashFunction(decommitments[decommitmentIndex], hash) + + index++ + } else if (index < upperBound) { + appendHashes[writeIndex++] = options.hashFunction(appendHashes[readIndex++], appendHashes[readIndex++]) + index += 2 + } + + if (index >= upperBound) { + if (index === upperBound) appendHashes[writeIndex] = appendHashes[readIndex] + + readIndex = 0 + writeIndex = 0 + upperBound >>>= 1 + offset >>>= 1 + index = offset + } + } + + return { root: hash, newRoot: appendHashes[0], elementCount } +} + +export const getNewRoot = (params: getNewRootParams, options: treeOptions = defaultTreeOptions): { root: Buffer, newRoot: Buffer, elementCount: number } => { + return params.appendLeaf?.length > 0 + ? getNewRootSingle(params.appendLeaf, params.compactProof, params.elementCount, params.decommitments, options) + : getNewRootMulti(params.appendLeafs, params.compactProof, params.elementCount, params.decommitments, options) +} + +// This is identical to getNewRootSingle, but it does not compute the old root. +// See getNewRootSingle for relevant inline comments. +export const appendSingle = (appendLeaf: Buffer, elementCount: number, decommitments: Array, options: treeOptions = defaultTreeOptions): Buffer => { + let index = bitCount32(elementCount) + let appendHash = appendLeaf + + while (index > 0) { + appendHash = options.hashFunction(decommitments[--index], appendHash) + } + + return appendHash +} + +// This is identical to getNewRootMulti, but it does not compute the old root. +// See getNewRootMulti for relevant inline comments. +export const appendMulti = (appendLeafs: Array, elementCount: number, decommitments: Array, options: treeOptions = defaultTreeOptions): Buffer => { + let decommitmentIndex = bitCount32(elementCount) - 1 + let appendHashes = appendLeafs.map((leaf) => leaf) + let upperBound = elementCount + appendLeafs.length - 1 + let writeIndex = 0 + let readIndex = 0 + let offset = elementCount + let index = offset + + while (upperBound > 0) { + if (writeIndex === 0 && index & 1) { + appendHashes[writeIndex++] = options.hashFunction(decommitments[decommitmentIndex--], appendHashes[readIndex++]) + index++ + } else if (index < upperBound) { + appendHashes[writeIndex++] = options.hashFunction(appendHashes[readIndex++], appendHashes[readIndex++]) + index += 2 + } + + if (index >= upperBound) { + if (index === upperBound) appendHashes[writeIndex] = appendHashes[readIndex] + + readIndex = 0 + writeIndex = 0 + upperBound >>>= 1 + offset >>>= 1 + index = offset + } + } + + return appendHashes[0] +} diff --git a/js/src/ts/combined-proofs.ts b/js/src/ts/combined-proofs.ts new file mode 100644 index 0000000..2c55115 --- /dev/null +++ b/js/src/ts/combined-proofs.ts @@ -0,0 +1,523 @@ +import { bitCount32, from32ByteBuffer, bufferToBigInt } from './utils' +import { generate as generateMulti } from './flag-multi-proofs' +import { generate as generateSingle } from './single-proofs' +import { defaultProofOptions, defaultTreeOptions, getNewRootParams, getRootParams, proof, proofOptions, treeOptions } from './common' + +export const generate = (tree: Array, elementCount: number, indices?: Array, index?: number, options: proofOptions = defaultProofOptions): proof => { + return index == null + ? generateMulti(tree, elementCount, indices, options) + : generateSingle(tree, elementCount, index, options) +} + +// This is the MultiFlagProof.getRootBooleans algorithm, however, it additionally infers and +// verifies the decommitments needed for the append-proof, as the provided decommitments for the +// multi-proof are verified. In order for the correct append-proof decommitments to be inferred, +// the multi-proof must be proving the existence of one of last elements. Two roots will be +// computed: one from the multi-proof and one from the inferred append-proof decommitments. They +// should match, so long as the multi-proof is valid, and on of the last element is being proved. +// The algorithm to infer the append-proof decommitments is to take the left node of each +// hashing pair, if the right node of the hashing pair is to be the appending node. +// See MultiFlagProof.getRootBooleans for relevant inline comments. +const getRootBooleansFromMulti = (leafs: Array, elementCount: number, flags: Array<1 | 0>, orders: Array<1 | 0>, skips: Array<1 | 0>, decommitments: Array, options: treeOptions = defaultTreeOptions): { root: Buffer, elementCount: number, appendDecommitments: Array } => { + const hashCount = flags.length + const leafCount = leafs.length + const hashes = leafs.map((leaf) => leaf).reverse() + + let readIndex = 0 + let writeIndex = 0 + let decommitmentIndex = 0 + + // The index, localized to the level/depth, of where the first appended element will go + let appendNodeIndex = elementCount + + // Since hashes is a circular queue, we need to remember where the "right-most" hash is + let readIndexOfAppendNode = 0 + + // We need as many append-proof decommitments as bits set in elementCount + // and we will build this array in reverse order + let appendDecommitmentIndex = bitCount32(elementCount) + const appendDecommitments = Array(appendDecommitmentIndex).fill(null) + + // We will be accumulating the computed append-proof inferred root here + let appendHash + + for (let i = 0; i < hashCount; i++) { + if (skips[i]) { + // If we're skipping, we're definitely dealing with the last node on this level, and it is + // an append-proof decommitment if this index is odd. Note two important things. First, for + // all unbalanced trees, the first append-proof decommitment is from here, and it will be + // the only append-proof decommitment taken from a "skipped" hash. Second, again for unbalanced + // trees, appendNodeIndex is referencing the non-existent leaf to be added, when elementCount + // is odd. When elementCount is even, it will be referencing an existing "right-most" node. + const skippedHash = hashes[readIndex++] + + if (appendNodeIndex & 1) { + appendDecommitments[--appendDecommitmentIndex] = skippedHash + + // Since we know this will always be the first append decommitment, appendHash starts as it + appendHash = skippedHash + } + + // Remember this circular queue index so we can tell when we've at the end of a new level + readIndexOfAppendNode = writeIndex + + // The index is localized to the level/depth, so the next one is it divided by 2 + appendNodeIndex >>>= 1 + + hashes[writeIndex++] = skippedHash + + readIndex %= leafCount + writeIndex %= leafCount + continue + } + + // Check if we're at the last ("right-most") node at a level (within the circular queue) + if (readIndexOfAppendNode === readIndex) { + // Only the hash sibling of odd "right-most" nodes are valid append-proof decommitments + if (appendNodeIndex & 1) { + const nextReadIndex = (readIndex + 1) % leafCount + + // Note: we can save variables here by swapping flag/decommitment inclusion from "right" + // to "left" below, and using left as the appendDecommitment, if hash order is not relevant. + const appendDecommitment = flags[i] ? hashes[nextReadIndex] : decommitments[decommitmentIndex] + + // flag informs if the "left" node is a previously computed hash, or a decommitment + appendDecommitments[--appendDecommitmentIndex] = appendDecommitment + + // Accumulate the into the append hash + appendHash = options.hashFunction(appendDecommitment, appendHash) + } + + // Remember this circular queue index so we can tell when we've at the end of a new level + readIndexOfAppendNode = writeIndex + + // The index is localized to the level/depth, so the next one is it divided by 2 + appendNodeIndex >>>= 1 + } + + const right = flags[i] ? hashes[readIndex++] : decommitments[decommitmentIndex++] + readIndex %= leafCount + const left = hashes[readIndex++] + hashes[writeIndex++] = orders?.[i] ? options.hashFunction(left, right) : options.hashFunction(right, left) + + readIndex %= leafCount + writeIndex %= leafCount + } + + const root = hashes[(writeIndex === 0 ? leafCount : writeIndex) - 1] + + // For a balanced tree, there is only 1 append-proof decommitment: the root itself + if (appendDecommitmentIndex !== 1 && appendHash.equals(root)) { + throw new Error('Invalid Proof.') + } + + // The new append decommitments is simply the new root, for a balanced tree. + if (appendDecommitmentIndex === 1) appendDecommitments[0] = root + + return { root: Buffer.from(root), elementCount, appendDecommitments } +} + +// This is identical to the above getRootBooleans algorithm, differing only in that the +// the flag and skip bit-set is shifted and checked, rather than boolean arrays. +// See getRootBooleans for relevant inline comments. +const getRootBitsFromMulti = (leafs: Array, compactProof: Array, options: treeOptions = defaultTreeOptions): { root: Buffer, elementCount: number, appendDecommitments: Array } => { + const elementCount = from32ByteBuffer(compactProof[0]) + const flags = bufferToBigInt(compactProof[1]) + const skips = bufferToBigInt(compactProof[2]) + const orders = options.sortedHash ? undefined : bufferToBigInt(compactProof[3]) + const decommitments = compactProof.slice(options.sortedHash ? 3 : 4) + const leafCount = leafs.length + const hashes = leafs.map((leaf) => leaf).reverse() + + let readIndex = 0 + let writeIndex = 0 + let decommitmentIndex = 0 + let bitCheck = BigInt(1) + + let appendNodeIndex = elementCount + let readIndexOfAppendNode = 0 + let appendDecommitmentIndex = bitCount32(elementCount) + const appendDecommitments = Array(appendDecommitmentIndex).fill(null) + let appendHash + + while (true) { + const flag = flags & bitCheck + + if (skips & bitCheck) { + if (flag) { + const root = hashes[(writeIndex === 0 ? leafCount : writeIndex) - 1] + + if (appendDecommitmentIndex !== 1 && appendHash.equals(root)) throw new Error('Invalid Proof.') + + if (appendDecommitmentIndex === 1) appendDecommitments[0] = root + + return { root: Buffer.from(root), elementCount, appendDecommitments } + } + + const skippedHash = hashes[readIndex++] + + if (appendNodeIndex & 1) { + appendDecommitments[--appendDecommitmentIndex] = skippedHash + appendHash = skippedHash + } + + readIndexOfAppendNode = writeIndex + appendNodeIndex >>>= 1 + + hashes[writeIndex++] = skippedHash + + readIndex %= leafCount + writeIndex %= leafCount + bitCheck <<= BigInt(1) + continue + } + + if (readIndexOfAppendNode === readIndex) { + const nextReadIndex = (readIndex + 1) % leafCount + const appendDecommitment = flag ? hashes[nextReadIndex] : decommitments[decommitmentIndex] + + if (appendNodeIndex & 1) { + appendDecommitments[--appendDecommitmentIndex] = appendDecommitment + appendHash = options.hashFunction(appendDecommitment, appendHash) + } + + readIndexOfAppendNode = writeIndex + appendNodeIndex >>>= 1 + } + + const right = flag ? hashes[readIndex++] : decommitments[decommitmentIndex++] + readIndex %= leafCount + const left = hashes[readIndex++] + + const order = orders && orders & bitCheck + hashes[writeIndex++] = order ? options.hashFunction(left, right) : options.hashFunction(right, left) + + readIndex %= leafCount + writeIndex %= leafCount + bitCheck <<= BigInt(1) + } +} + +// This is the SingleProof.getRoot algorithm, however, it additionally infers and verifies the +// decommitments needed for the append-proof, as the provided decommitments for the single proof +// are verified. It is effectively a combination of the SingleProof.getRoot and the above +// getRootBooleansFromMulti. +// See MultiFlagProof.getRootBooleans and getRootBooleansFromMulti for relevant inline comments. +const getRootFromSingle = (index: number, leaf: Buffer, compactProof: Array, elementCount: number, decommitments: Array, options: treeOptions = defaultTreeOptions): { root: Buffer, elementCount: number, appendDecommitments: Array } => { + if (compactProof.length > 0) { + elementCount = from32ByteBuffer(compactProof[0]) + decommitments = compactProof.slice(1) + } + + let decommitmentIndex = decommitments.length + let hash = Buffer.from(leaf) + let upperBound = elementCount - 1 + let appendNodeIndex = elementCount + let appendDecommitmentIndex = bitCount32(elementCount) + const appendDecommitments = Array(appendDecommitmentIndex).fill(null) + let appendHash + + while (decommitmentIndex > 0) { + if (index === upperBound && !(index & 1)) { + if (appendNodeIndex & 1) { + appendDecommitments[--appendDecommitmentIndex] = hash + appendHash = hash + } + + index >>>= 1 + upperBound >>>= 1 + appendNodeIndex >>>= 1 + continue + } + + --decommitmentIndex + + if (appendNodeIndex & 1) { + appendDecommitments[--appendDecommitmentIndex] = decommitments[decommitmentIndex] + appendHash = options.hashFunction(decommitments[decommitmentIndex], appendHash) + } + + hash = + index & 1 + ? options.hashFunction(decommitments[decommitmentIndex], hash) + : options.hashFunction(hash, decommitments[decommitmentIndex]) + + index >>>= 1 + upperBound >>>= 1 + appendNodeIndex >>>= 1 + } + + if (appendDecommitmentIndex !== 1 && appendHash.equals(hash)) throw new Error('Invalid Proof.') + + if (appendDecommitmentIndex === 1) appendDecommitments[0] = hash + + return { root: hash, elementCount, appendDecommitments } +} + +export const getRoot = (params: getRootParams, options: treeOptions = defaultTreeOptions): { root: Buffer, elementCount: number, appendDecommitments: Array } => { + return params.leaf instanceof Buffer + ? getRootFromSingle(params.index, params.leaf, params.compactProof, params.elementCount, params.decommitments, options) + : params.compactProof.length > 0 + ? getRootBitsFromMulti(params.leafs, params.compactProof, options) + : getRootBooleansFromMulti(params.leafs, params.elementCount, params.flags, params.orders, params.skips, params.decommitments, options) +} + +// This is identical to the above getRootBooleans followed by the AppendProof.getNewRootMulti. +// First, a loop computes the new root, given the decommitments and update elements. At the same +// time, the old root is computed, from the decommitments and original elements. Also, at the +// same time, the old root is computed, from the inferred append-proof decommitments. And also, +// at the same time, the new append-proof decommitments are computed from the updated elements. +// Finally either appendMulti or appendSingle above is called to get the new root. +// See getRootBooleans for relevant inline comments. +const getNewRootBooleansFromMulti = (leafs: Array, updateLeafs: Array, elementCount: number, flags: Array<1 | 0>, skips: Array<1 | 0>, orders: Array<1 | 0>, decommitments: Array, options: treeOptions = defaultTreeOptions): { root: Buffer, newRoot: Buffer, elementCount: number, appendDecommitments: Array } => { + const hashCount = flags.length + const leafCount = leafs.length + const hashes = leafs.map((leaf) => leaf).reverse() + const newHashes = updateLeafs.map((leaf) => leaf).reverse() + + let readIndex = 0 + let writeIndex = 0 + let decommitmentIndex = 0 + let appendNodeIndex = elementCount + let readIndexOfAppendNode = 0 + let appendDecommitmentIndex = bitCount32(elementCount) + const appendDecommitments = Array(appendDecommitmentIndex).fill(null) + let hash + + for (let i = 0; i < hashCount; i++) { + if (skips[i]) { + const skippedHash = hashes[readIndex] + const newSkippedHash = newHashes[readIndex++] + + if (appendNodeIndex & 1) { + // decommitments for the append step are actually the new hashes, given the updated leafs. + appendDecommitments[--appendDecommitmentIndex] = newSkippedHash + + // hash still needs to accumulate old values, to result in old root. + hash = skippedHash + } + + readIndexOfAppendNode = writeIndex + appendNodeIndex >>>= 1 + + hashes[writeIndex] = skippedHash + newHashes[writeIndex++] = newSkippedHash + + readIndex %= leafCount + writeIndex %= leafCount + continue + } + + if (readIndexOfAppendNode === readIndex) { + if (appendNodeIndex & 1) { + const nextReadIndex = (readIndex + 1) % leafCount + const appendHash = flags[i] ? hashes[nextReadIndex] : decommitments[decommitmentIndex] + const newAppendHash = flags[i] ? newHashes[nextReadIndex] : decommitments[decommitmentIndex] + + // decommitments for the append step are actually the new hashes, given the updated leafs. + appendDecommitments[--appendDecommitmentIndex] = newAppendHash + + // hash still needs to accumulate old values, to result in old root. + hash = options.hashFunction(appendHash, hash) + } + + readIndexOfAppendNode = writeIndex + appendNodeIndex >>>= 1 + } + + const right = flags[i] ? hashes[readIndex] : decommitments[decommitmentIndex] + const newRight = flags[i] ? newHashes[readIndex++] : decommitments[decommitmentIndex++] + + readIndex %= leafCount + + const left = hashes[readIndex] + const newLeft = newHashes[readIndex++] + + hashes[writeIndex] = orders?.[i] ? options.hashFunction(left, right) : options.hashFunction(right, left) + newHashes[writeIndex++] = orders?.[i] ? options.hashFunction(newLeft, newRight) : options.hashFunction(newRight, newLeft) + + readIndex %= leafCount + writeIndex %= leafCount + } + + const rootIndex = (writeIndex === 0 ? leafCount : writeIndex) - 1 + const oldRoot = hashes[rootIndex] + const newRoot = newHashes[rootIndex] + + if (appendDecommitmentIndex !== 1 && hash.equals(oldRoot)) throw new Error('Invalid Proof.') + + if (appendDecommitmentIndex === 1) appendDecommitments[0] = newRoot + + return { root: Buffer.from(oldRoot), newRoot, elementCount, appendDecommitments } +} + +// This is identical to the above getNewRootBooleans algorithm, differing only in that the +// the flag and skip bit-set is shifted and checked, rather than boolean arrays. +// See getNewRootBooleans for relevant inline comments. +const getNewRootBitsFromMulti = (leafs: Array, updateLeafs: Array, compactProof: Array, options: treeOptions = defaultTreeOptions): { root: Buffer, newRoot: Buffer, elementCount: number, appendDecommitments: Array } => { + const elementCount = from32ByteBuffer(compactProof[0]) + const flags = bufferToBigInt(compactProof[1]) + const skips = bufferToBigInt(compactProof[2]) + const orders = options.sortedHash ? undefined : bufferToBigInt(compactProof[3]) + const decommitments = compactProof.slice(options.sortedHash ? 3 : 4) + const leafCount = leafs.length + const hashes = leafs.map((leaf) => leaf).reverse() + const newHashes = updateLeafs.map((leaf) => leaf).reverse() + + let readIndex = 0 + let writeIndex = 0 + let decommitmentIndex = 0 + let bitCheck = BigInt(1) + let appendNodeIndex = elementCount + let readIndexOfAppendNode = 0 + let appendDecommitmentIndex = bitCount32(elementCount) + const appendDecommitments = Array(appendDecommitmentIndex).fill(null) + let hash + + while (true) { + const flag = flags & bitCheck + + if (skips & bitCheck) { + if (flag) { + const rootIndex = (writeIndex === 0 ? leafCount : writeIndex) - 1 + const oldRoot = hashes[rootIndex] + const newRoot = newHashes[rootIndex] + + if (appendDecommitmentIndex !== 1 && hash.equals(oldRoot)) throw new Error('Invalid Proof.') + + if (appendDecommitmentIndex === 1) appendDecommitments[0] = newRoot + + return { root: oldRoot, newRoot, elementCount, appendDecommitments } + } + + const skippedHash = hashes[readIndex] + const newSkippedHash = newHashes[readIndex++] + + if (appendNodeIndex & 1) { + appendDecommitments[--appendDecommitmentIndex] = newSkippedHash + hash = skippedHash + } + + readIndexOfAppendNode = writeIndex + appendNodeIndex >>>= 1 + + hashes[writeIndex] = skippedHash + newHashes[writeIndex++] = newSkippedHash + + readIndex %= leafCount + writeIndex %= leafCount + bitCheck <<= BigInt(1) + continue + } + + if (readIndexOfAppendNode === readIndex) { + if (appendNodeIndex & 1) { + const nextReadIndex = (readIndex + 1) % leafCount + const appendHash = flag ? hashes[nextReadIndex] : decommitments[decommitmentIndex] + const newAppendHash = flag ? newHashes[nextReadIndex] : decommitments[decommitmentIndex] + + appendDecommitments[--appendDecommitmentIndex] = newAppendHash + hash = options.hashFunction(appendHash, hash) + } + + readIndexOfAppendNode = writeIndex + appendNodeIndex >>>= 1 + } + + const right = flag ? hashes[readIndex] : decommitments[decommitmentIndex] + const newRight = flag ? newHashes[readIndex++] : decommitments[decommitmentIndex++] + + readIndex %= leafCount + + const left = hashes[readIndex] + const newLeft = newHashes[readIndex++] + + const order = orders && orders & bitCheck + hashes[writeIndex] = order ? options.hashFunction(left, right) : options.hashFunction(right, left) + newHashes[writeIndex++] = order ? options.hashFunction(newLeft, newRight) : options.hashFunction(newRight, newLeft) + + readIndex %= leafCount + writeIndex %= leafCount + bitCheck <<= BigInt(1) + } +} + +// This is identical to the above getRootFromSingle algorithm, except the append decommitments are +// built taking the update leaf into account, followed by just calling the appendMulti or +// appendSingle above to get the new root. +// See getRootFromSingle for relevant inline comments. +const getNewRootFromSingle = (index: number, leaf: Buffer, updateLeaf: Buffer, compactProof: Array, elementCount: number, decommitments: Array, options: treeOptions = defaultTreeOptions): { root: Buffer, newRoot: Buffer, elementCount: number, appendDecommitments: Array } => { + if (compactProof.length > 0) { + elementCount = from32ByteBuffer(compactProof[0]) + decommitments = compactProof.slice(1) + } + + let decommitmentIndex = decommitments.length + let hash = Buffer.from(leaf) + let updateHash = Buffer.from(updateLeaf) + let upperBound = elementCount - 1 + let appendNodeIndex = elementCount + let appendDecommitmentIndex = bitCount32(elementCount) + const appendDecommitments = Array(appendDecommitmentIndex).fill(null) + let appendHash + + while (decommitmentIndex > 0) { + if (index === upperBound && !(index & 1)) { + if (appendNodeIndex & 1) { + appendDecommitments[--appendDecommitmentIndex] = updateHash + appendHash = hash + } + + index >>>= 1 + upperBound >>>= 1 + appendNodeIndex >>>= 1 + continue + } + + --decommitmentIndex + + if (appendNodeIndex & 1) { + appendDecommitments[--appendDecommitmentIndex] = decommitments[decommitmentIndex] + appendHash = options.hashFunction(decommitments[decommitmentIndex], appendHash) + } + + hash = + index & 1 + ? options.hashFunction(decommitments[decommitmentIndex], hash) + : options.hashFunction(hash, decommitments[decommitmentIndex]) + + updateHash = + index & 1 + ? options.hashFunction(decommitments[decommitmentIndex], updateHash) + : options.hashFunction(updateHash, decommitments[decommitmentIndex]) + + index >>>= 1 + upperBound >>>= 1 + appendNodeIndex >>>= 1 + } + + if (appendDecommitmentIndex !== 1 && hash.equals(hash)) throw new Error('Invalid Proof.') + + if (appendDecommitmentIndex === 1) appendDecommitments[0] = updateHash + + return { root: hash, newRoot: updateHash, elementCount, appendDecommitments } +} + +export const getNewRoot = (params: getNewRootParams, options: treeOptions = defaultTreeOptions) => { + return params.leaf + ? getNewRootFromSingle(params.index, params.leaf, params.updateLeaf, params.compactProof, params.elementCount, params.decommitments, options) + : params.compactProof.length > 0 + ? getNewRootBitsFromMulti(params.leafs, params.updateLeafs, params.compactProof, options) + : getNewRootBooleansFromMulti(params.leafs, params.updateLeafs, params.elementCount, params.flags, params.skips, params.orders, params.decommitments, options) +} + +// This returns the minimum index that must be in the proof, to result in a proof that will be +// a valid combined proof (i.e. a valid multi-proof and append-proof). Simply, set the first +// set bit in the element count to zero, and return that value. +export const getMinimumIndex = (elementCount: number): number => { + for (let shifts = 0; shifts < 32; shifts++) { + if (elementCount & 1) return (elementCount & 0xfffffffe) << shifts + + elementCount >>>= 1 + } +} diff --git a/js/src/ts/common.ts b/js/src/ts/common.ts new file mode 100644 index 0000000..0ef70e3 --- /dev/null +++ b/js/src/ts/common.ts @@ -0,0 +1,200 @@ +import { roundUpToPowerOf2, hashNode } from "./utils" + +export interface proofOptions { + compact?: boolean, + simple?: boolean, + indexed?: boolean, + unbalanced?: boolean, + sortedHash?: boolean, + elementPrefix?: string +} + +export const defaultProofOptions: proofOptions = { + compact: false, + simple: false, + indexed: false, + unbalanced: true, + sortedHash: false, + elementPrefix: '00' +} + +export interface treeOptions { + unbalanced?: boolean, + sortedHash?: boolean, + hashFunction?: (left: Buffer, right: Buffer) => Buffer +} + +export const defaultTreeOptions: treeOptions = { + unbalanced: true, + sortedHash: false, + hashFunction: hashNode +} + +export interface base { + index?: number, + indices?: Array, + + elementCount?: number, + + compactProof?: Array, + decommitments?: Array, + + flags?: Array<1 | 0>, + orders?: Array<1 | 0>, + skips?: Array<1 | 0>, +} + +export interface getRootParams extends base { + leaf?: Buffer, + leafs?: Array, +} + +export interface getNewRootParams extends getRootParams { + appendLeaf?: Buffer, + appendLeafs?: Array, + updateLeaf?: Buffer + updateLeafs?: Array, +} + +export interface proof extends base { + root?: Buffer, + + element?: Buffer, + elements?: Array, +} + +export interface updateProof extends proof { + updateElement?: Buffer, + updateElements?: Array, +} + +export interface appendProof extends proof { + appendElement?: Buffer, + appendElements?: Array, +} + +export interface updateAndAppendProof extends proof { + updateElement?: Buffer, + updateElements?: Array, + appendElement?: Buffer, + appendElements?: Array, +} + + +export const getDepth = (elementCount: number): number => { + return Math.ceil(Math.log2(elementCount)) +} + +export const getBalancedLeafCount = (elementCount: number): number => { + return roundUpToPowerOf2(elementCount) +} + +export const buildTree = (leafs: Array, options: treeOptions = defaultTreeOptions): { tree: Array, depth: number } => { + const depth = getDepth(leafs.length) + const balancedLeafCount = getBalancedLeafCount(leafs.length) + const tree = Array(balancedLeafCount << 1).fill(null) + + for (let i = 0; i < leafs.length; i++) { + tree[balancedLeafCount + i] = leafs[i] + } + + let lowerBound = balancedLeafCount + let upperBound = balancedLeafCount + leafs.length - 1 + + for (let i = balancedLeafCount - 1; i > 0; i--) { + const index = i << 1 + + if (index > upperBound) continue + + if (index <= lowerBound) { + lowerBound >>>= 1 + upperBound >>>= 1 + } + + if (index === upperBound) { + tree[i] = tree[index] + continue + } + + tree[i] = options.hashFunction(tree[index], tree[index + 1]) + } + + return { tree, depth } +} + +export const checkElement = (tree: Array, index: number, leaf: Buffer): boolean => { + const localLeaf = tree[(tree.length >> 1) + index] + + return localLeaf ? localLeaf.equals(leaf) : false +} + +export const checkElements = (tree: Array, indices: Array, leafs: Array): Array => { + return indices.reduce((exists, index, i) => exists.concat(checkElement(tree, index, leafs[i])), []) +} + +export const getUpdatedTree = (tree: Array, leafs: Array, options = defaultTreeOptions): Array => { + const balancedLeafCount = tree.length >> 1 + const newTree = tree.map((n) => n && Buffer.from(n)) + + for (let i = 0; i < leafs.length; i++) { + if (leafs[i]) { + newTree[balancedLeafCount + i] = leafs[i] + } + } + + let lowerBound = balancedLeafCount + let upperBound = balancedLeafCount + leafs.length - 1 + + for (let i = balancedLeafCount - 1; i > 0; i--) { + const index = i << 1 + + if (index > upperBound) continue + + if (index <= lowerBound) { + lowerBound >>>= 1 + upperBound >>>= 1 + } + + if (index === upperBound) { + if (newTree[index]) { + newTree[i] = newTree[index] + } + + continue + } + + if (!newTree[index] && !newTree[index + 1]) continue + + try { + newTree[i] = options.hashFunction(newTree[index], newTree[index + 1]) + } catch { + throw Error('Insufficient information to build tree.') + } + } + + return newTree +} + +export const getGrownTree = (tree: Array, leafs: Array, options = defaultTreeOptions): Array => { + const oldDepth = getDepth(tree.length >> 1) + const oldBalancedLeafCount = tree.length >> 1 + const depth = getDepth(leafs.length) + const balancedLeafCount = getBalancedLeafCount(leafs.length) + if (balancedLeafCount < oldBalancedLeafCount) { + throw new Error('Tree is already larger') + } + + const newTree = Array(balancedLeafCount << 1).fill(null) + + for (let i = 0; i < leafs.length; i++) { + newTree[balancedLeafCount + i] = tree[oldBalancedLeafCount + i] ?? null + } + + for (let i = 1; i <= oldDepth; i++) { + for (let j = 0; j < leafs.length >> i; j++) { + newTree[(balancedLeafCount >> i) + j] = tree[(oldBalancedLeafCount >> i) + j] + } + } + + return getUpdatedTree(newTree, leafs, options) +} diff --git a/js/src/ts/flag-multi-proofs.ts b/js/src/ts/flag-multi-proofs.ts new file mode 100644 index 0000000..0c0197d --- /dev/null +++ b/js/src/ts/flag-multi-proofs.ts @@ -0,0 +1,445 @@ +import { defaultProofOptions, defaultTreeOptions, getNewRootParams, getRootParams, proof, proofOptions, treeOptions } from './common' +import { + to32ByteBuffer, + from32ByteBuffer, + to32ByteBoolBuffer, + toBigIntBoolSet, + bigIntTo32ByteBuffer, + bufferToBigInt, +} from './utils' + +// This is the MultiIndexedProof.generate algorithm, however, since indices will not be used to +// compute the root at verify-time, a set fo flags need to be generated to indicate, for each +// hash performed at verify-time, whether a previously computed hash will be needed (True), or +// a decommitment will be needed. Since this method only works with hash functions that sort the +// hashed material, there is no need to provide instructions on hashing order. However, such a +// proof would also be possible, with a separate set of flags to instruct the hashing order. +// See MultiIndexedProof.generate for relevant inline comments. +const generateBooleans = (tree: Array, elementCount: number, indices: Array, options: proofOptions = defaultProofOptions): proof => { + const known = Array<1 | 0>(tree.length).fill(0) + const relevant = Array<1 | 0>(tree.length).fill(0) + const decommitments = Array() + const flags = Array<1 | 0>() + const orders = Array<1 | 0>() + const skips = Array<1 | 0>() + const leafCount = tree.length >>> 1 + + for (let i = 0; i < indices.length; i++) { + if (i !== 0 && indices[i - 1] > indices[i]) { + throw new Error('Indices must be in ascending order.') + } + known[leafCount + indices[i]] = 1 + + // The parent of this node is relevant, as there will be a hash computed at verify-time. + relevant[(leafCount + indices[i]) >>> 1] = 1 + } + + for (let i = leafCount - 1; i > 0; i--) { + const leftChildIndex = i << 1 + const left = known[leftChildIndex] ? 1 : 0 + const right = known[leftChildIndex + 1] ? 1 : 0 + const sibling = tree[leftChildIndex + left] + + if (left ^ right) decommitments.push(sibling) + + // Since there will be a hash computed at verify-time, push the flag on wether this hash + // will require a decommitment (False) or a previously computed hash (True). Also, if the + // sibling of this child does not exist, the sibling must be to the "right" of the + // "right-most" leaf, so the hash can be skipped in favor of just using the child itself. + // Further, the parent of this node it itself relevant, in a subsequent iteration. + if (relevant[i]) { + flags.push(left === right ? 1 : 0) + skips.push(!sibling ? 1 : 0) + orders.push(left) + relevant[i >>> 1] = 1 + } + + known[i] = left || right + } + + return { + compactProof: Array(), + elementCount, + decommitments: decommitments.filter((d) => d).map(Buffer.from), + flags, + skips, + orders: !options.sortedHash ? orders : undefined, + } +} + +// Convert the flags, skips, and orders generated by generateBooleans into a 32-byte bit-set +const generateBits = (tree: Array, elemCount: number, indices: Array, options: proofOptions = defaultProofOptions): proof => { + const { elementCount, decommitments, flags, orders, skips } = generateBooleans(tree, elemCount, indices, options) + + if (flags.length > 255) { + throw new Error('Proof too large for bit flags.') + } + + const stopMask = BigInt(1) << BigInt(flags.length) + const proof = orders ? [to32ByteBoolBuffer(orders)].concat(decommitments) : decommitments + const flagsAsBits = bigIntTo32ByteBuffer(toBigIntBoolSet(flags) | stopMask) + const skipsAsBits = bigIntTo32ByteBuffer(toBigIntBoolSet(skips) | stopMask) + + return { + compactProof: [to32ByteBuffer(elementCount), flagsAsBits, skipsAsBits].concat(proof), + elementCount, + decommitments: Array(), + flags: Array<1 | 0>(), + skips: Array<1 | 0>(), + orders: undefined + } +} + +export const generate = (tree: Array, elemCount: number, indices: Array, options: proofOptions = defaultProofOptions): proof => { + return options.compact + ? generateBits(tree, elemCount, indices, options) + : generateBooleans(tree, elemCount, indices, options) +} + +// This is the MultiIndexedProof.getRoot algorithm, slightly simplified to take into account that +// this is to be used with a hash function that sorts the material it hashes, and thus this uses flags +// to determine hashing content, instead of the indices. Further, this implements skipping hashing for +// nodes without siblings to the "right", in the case of unbalanced trees. +// See MultiIndexedProof.getRoot for relevant inline comments. +const getRootBooleans = (leafs: Array, elementCount: number, flags: Array<1 | 0>, skips: Array<1 | 0>, orders: Array<1 | 0>, decommitments: Array, options: treeOptions = defaultTreeOptions): { root: Buffer, elementCount: number } => { + const hashCount = flags.length + const leafCount = leafs.length + const hashes = leafs.map((leaf) => leaf).reverse() + + let readIndex = 0 + let writeIndex = 0 + let decommitmentIndex = 0 + + for (let i = 0; i < hashCount; i++) { + if (skips[i]) { + hashes[writeIndex++] = hashes[readIndex++] + + readIndex %= leafCount + writeIndex %= leafCount + continue + } + + const right = flags[i] ? hashes[readIndex++] : decommitments[decommitmentIndex++] + readIndex %= leafCount + const left = hashes[readIndex++] + hashes[writeIndex++] = orders?.[i] ? options.hashFunction(left, right) : options.hashFunction(right, left) + + readIndex %= leafCount + writeIndex %= leafCount + } + + const rootIndex = (writeIndex === 0 ? leafCount : writeIndex) - 1 + + return { root: Buffer.from(hashes[rootIndex]), elementCount } +} + +// This is identical to the above getRootBooleans algorithm, differing only in that the +// the flag and skip bit-set is shifted and checked, rather than boolean arrays. +// See getRootBooleans for relevant inline comments. +const getRootBits = (leafs: Array, compactProof: Array, options: treeOptions = defaultTreeOptions): { root: Buffer, elementCount: number } => { + const elementCount = from32ByteBuffer(compactProof[0]) + const flags = bufferToBigInt(compactProof[1]) + const skips = bufferToBigInt(compactProof[2]) + const orders = options.sortedHash ? undefined : bufferToBigInt(compactProof[3]) + const decommitments = compactProof.slice(options.sortedHash ? 3 : 4) + const leafCount = leafs.length + const hashes = leafs.map((leaf) => leaf).reverse() + + let readIndex = 0 + let writeIndex = 0 + let decommitmentIndex = 0 + let bitCheck = BigInt(1) + + while (true) { + const flag = flags & bitCheck + + if (skips & bitCheck) { + if (flag) { + const rootIndex = (writeIndex === 0 ? leafCount : writeIndex) - 1 + + return { root: hashes[rootIndex], elementCount } + } + + hashes[writeIndex++] = hashes[readIndex++] + + readIndex %= leafCount + writeIndex %= leafCount + bitCheck <<= BigInt(1) + continue + } + + const right = flag ? hashes[readIndex++] : decommitments[decommitmentIndex++] + readIndex %= leafCount + const left = hashes[readIndex++] + + const order = orders && orders & bitCheck + hashes[writeIndex++] = order ? options.hashFunction(left, right) : options.hashFunction(right, left) + + readIndex %= leafCount + writeIndex %= leafCount + bitCheck <<= BigInt(1) + } +} + +export const getRoot = (params: getRootParams, options: treeOptions = defaultTreeOptions): { root: Buffer, elementCount: number } => { + return params.compactProof.length > 0 + ? getRootBits(params.leafs, params.compactProof, options) + : getRootBooleans(params.leafs, params.elementCount, params.flags, params.skips, params.orders, params.decommitments, options) +} + +// This is identical to the above getRootBooleans algorithm, differing only in that the +// new root (due to the updated leafs), is computed along the way. +// See getRootBooleans for relevant inline comments. +const getNewRootBooleans = (leafs: Array, updateLeafs: Array, elementCount: number = 0, flags: Array<1 | 0>, skips: Array<1 | 0>, orders: Array<1 | 0>, decommitments: Array, options: treeOptions = defaultTreeOptions): { root: Buffer, newRoot: Buffer, elementCount: number } => { + const hashCount = flags.length + const leafCount = leafs.length + const hashes = leafs.map((leaf) => leaf).reverse() + const updateHashes = updateLeafs.map((leaf) => leaf).reverse() + + let readIndex = 0 + let writeIndex = 0 + let decommitmentIndex = 0 + + for (let i = 0; i < hashCount; i++) { + if (skips[i]) { + hashes[writeIndex] = hashes[readIndex] + updateHashes[writeIndex++] = updateHashes[readIndex++] + + readIndex %= leafCount + writeIndex %= leafCount + continue + } + + const right = flags[i] ? hashes[readIndex] : decommitments[decommitmentIndex] + const newRight = flags[i] ? updateHashes[readIndex++] : decommitments[decommitmentIndex++] + readIndex %= leafCount + + const left = hashes[readIndex] + const newLeft = updateHashes[readIndex++] + hashes[writeIndex] = orders?.[i] ? options.hashFunction(left, right) : options.hashFunction(right, left) + updateHashes[writeIndex++] = orders?.[i] ? options.hashFunction(newLeft, newRight) : options.hashFunction(newRight, newLeft) + + readIndex %= leafCount + writeIndex %= leafCount + } + + const rootIndex = (writeIndex === 0 ? leafCount : writeIndex) - 1 + + return { + root: Buffer.from(hashes[rootIndex]), + newRoot: Buffer.from(updateHashes[rootIndex]), + elementCount, + } +} + +// This is identical to the above getRootBits algorithm, differing only in that the +// new root (due to the updated leafs), is computed along the way. +// See getRootBits for relevant inline comments. +const getNewRootBits = (leafs: Array, updateLeafs: Array, compactProof: Array, options: treeOptions = defaultTreeOptions): { root: Buffer, newRoot: Buffer, elementCount: number } => { + const elementCount = from32ByteBuffer(compactProof[0]) + const flags = bufferToBigInt(compactProof[1]) + const skips = bufferToBigInt(compactProof[2]) + const orders = options.sortedHash ? undefined : bufferToBigInt(compactProof[3]) + const decommitments = compactProof.slice(options.sortedHash ? 3 : 4) + const leafCount = leafs.length + const hashes = leafs.map((leaf) => leaf).reverse() + const updateHashes = updateLeafs.map((leaf) => leaf).reverse() + + let readIndex = 0 + let writeIndex = 0 + let decommitmentIndex = 0 + let bitCheck = BigInt(1) + + while (true) { + const flag = flags & bitCheck + + if (skips & bitCheck) { + if (flag) { + const rootIndex = (writeIndex === 0 ? leafCount : writeIndex) - 1 + + return { + root: Buffer.from(hashes[rootIndex]), + newRoot: Buffer.from(updateHashes[rootIndex]), + elementCount, + } + } + + hashes[writeIndex] = hashes[readIndex] + updateHashes[writeIndex++] = updateHashes[readIndex++] + + readIndex %= leafCount + writeIndex %= leafCount + bitCheck <<= BigInt(1) + continue + } + + const right = flag ? hashes[readIndex] : decommitments[decommitmentIndex] + const newRight = flag ? updateHashes[readIndex++] : decommitments[decommitmentIndex++] + readIndex %= leafCount + + const left = hashes[readIndex] + const newLeft = updateHashes[readIndex++] + + const order = orders && orders & bitCheck + hashes[writeIndex] = order ? options.hashFunction(left, right) : options.hashFunction(right, left) + updateHashes[writeIndex++] = order ? options.hashFunction(newLeft, newRight) : options.hashFunction(newRight, newLeft) + + readIndex %= leafCount + writeIndex %= leafCount + bitCheck <<= BigInt(1) + } +} + +export const getNewRoot = (params: getNewRootParams, options: treeOptions = defaultTreeOptions): { root: Buffer, newRoot: Buffer, elementCount: number } => { + return params.compactProof.length > 0 + ? getNewRootBits(params.leafs, params.updateLeafs, params.compactProof, options) + : getNewRootBooleans(params.leafs, params.updateLeafs, params.elementCount, params.flags, params.skips, params.orders, params.decommitments, options) +} + +// Infers the indices of a multi proof by back-calculating the the bits of each element's +// index, based on it's relative position in each hash operation during a proof. +const getIndicesWithBooleans = (leafCount: number, flags: Array<1 | 0>, skips: Array<1 | 0>, orders: Array<1 | 0>): { indices: Array } => { + if (orders.length == 0) { + throw new Error('Cannot infer indices without orders in proof.') + } + + const hashCount = flags.length + const indices = Array(leafCount).fill(0) + const groupedWithNext = Array(leafCount).fill(false) + const bitsPushed = Array(leafCount).fill(0) + let leafIndex = leafCount - 1 + + for (let i = 0; i < hashCount; i++) { + if (skips[i]) { + while (true) { + bitsPushed[leafIndex]++ + + if (leafIndex === 0) { + leafIndex = leafCount - 1 + break + } + + if (!groupedWithNext[leafIndex--]) break + } + + continue + } + + if (flags[i]) { + while (true) { + if (orders[i]) indices[leafIndex] |= 1 << bitsPushed[leafIndex] + + bitsPushed[leafIndex]++ + + if (leafIndex === 0) { + leafIndex = leafCount - 1 + break + } + + if (!groupedWithNext[leafIndex]) { + groupedWithNext[leafIndex--] = true + break + } + + groupedWithNext[leafIndex--] = true + } + } + + while (true) { + if (!orders[i]) indices[leafIndex] |= 1 << bitsPushed[leafIndex] + + bitsPushed[leafIndex]++ + + if (leafIndex === 0) { + leafIndex = leafCount - 1 + break + } + + if (!groupedWithNext[leafIndex--]) break + } + } + + return { indices } +} + +// This is identical to the above getIndicesWithBooleans, but with bit sets rather than +// boolean arrays. +// See getIndicesWithBooleans for relevant inline comments +const getIndicesWithBits = ( + leafCount: number, + compactProof: Array, + flags = bufferToBigInt(compactProof[1]), + skips = bufferToBigInt(compactProof[2]), + orders = bufferToBigInt(compactProof[3]), +): { indices: Array } => { + const indices = Array(leafCount).fill(0) + const groupedWithNext = Array(leafCount).fill(false) + const bitsPushed = Array(leafCount).fill(0) + let leafIndex = leafCount - 1 + let bitCheck = BigInt(1) + + while (true) { + const flag = flags & bitCheck + + if (skips & bitCheck) { + if (flag) return { indices } + + while (true) { + bitsPushed[leafIndex]++ + + if (leafIndex === 0) { + leafIndex = leafCount - 1 + break + } + + if (!groupedWithNext[leafIndex--]) break + } + + bitCheck <<= BigInt(1) + continue + } + + const order = orders & bitCheck + + if (flag) { + while (true) { + if (order) indices[leafIndex] |= 1 << bitsPushed[leafIndex] + + bitsPushed[leafIndex]++ + + if (leafIndex === 0) { + leafIndex = leafCount - 1 + break + } + + if (!groupedWithNext[leafIndex]) { + groupedWithNext[leafIndex--] = true + break + } + + groupedWithNext[leafIndex--] = true + } + } + + while (true) { + if (!order) indices[leafIndex] |= 1 << bitsPushed[leafIndex] + + bitsPushed[leafIndex]++ + + if (leafIndex === 0) { + leafIndex = leafCount - 1 + break + } + + if (!groupedWithNext[leafIndex--]) break + } + + bitCheck <<= BigInt(1) + } +} + +export const getIndices = (proof: proof): { indices: Array } => { + return proof.compactProof.length > 0 + ? getIndicesWithBits(proof.elements?.length, proof.compactProof) + : getIndicesWithBooleans(proof.elements?.length, proof.flags, proof.skips, proof.orders) +} diff --git a/js/src/ts/index-multi-proofs.ts b/js/src/ts/index-multi-proofs.ts new file mode 100644 index 0000000..3684407 --- /dev/null +++ b/js/src/ts/index-multi-proofs.ts @@ -0,0 +1,246 @@ +import { defaultProofOptions, defaultTreeOptions, getNewRootParams, getRootParams, proof, proofOptions, treeOptions } from './common' +import { to32ByteBuffer, from32ByteBuffer, roundUpToPowerOf2 } from './utils' + +// Generates a set of decommitments to prove the existence of leaves at a given indices. +// Accomplishes this by tracking the indices of the leafs in the serialized tree, and +// accumulates the decommitments if only one of the nodes, at any given level, would be +// known (provided as leafs) at verify-time. +export const generate = (tree: Array, elementCount: number, indices: Array, options: proofOptions = defaultProofOptions): proof => { + const known = Array(tree.length).fill(false) + const decommitments = [] + const leafCount = tree.length >>> 1 + + for (let i = 0; i < indices.length; i++) { + if (i !== 0 && indices[i - 1] > indices[i]) throw new Error('Indices must be in ascending order.') + known[leafCount + indices[i]] = true + } + + for (let i = leafCount - 1; i > 0; i--) { + const leftChildIndex = i << 1 + const left = known[leftChildIndex] + const right = known[leftChildIndex + 1] + + // Only one of children would be known, so we need the sibling as a decommitment + if (left ^ right) decommitments.push(tree[leftChildIndex + left]) + + // if at least one of the children is known, we would know the parent at verify-time + known[i] = left || right + } + + const clonedDecommitments = decommitments.filter((d) => d).map(Buffer.from) + + return options.compact + ? { + indices, + compactProof: [to32ByteBuffer(elementCount)].concat(clonedDecommitments), + elementCount: null, + decommitments: [] + } + : { + indices, + compactProof: [], + elementCount, + decommitments: clonedDecommitments, + } +} + +// Compute the root given a set of leafs, their indices, and a set of decommitments +// Uses a circular queue to accumulate the parent nodes and another circular to track +// the serialized tree indices of those nodes. +export const getRoot = (params: getRootParams, options: treeOptions = defaultTreeOptions): { root: Buffer, elementCount: number } => { + if (params.compactProof.length > 0) { + params.elementCount = from32ByteBuffer(params.compactProof[0]) + params.decommitments = params.compactProof.slice(1) + } + + const balancedLeafCount = roundUpToPowerOf2(params.elementCount) + + // Keep verification minimal by using circular hashes queue with separate read and write heads + const hashes = params.leafs.map((leaf) => leaf).reverse() + const treeIndices = params.indices.map((index) => balancedLeafCount + index).reverse() + const indexCount = params.indices.length + + let readIndex = 0 + let writeIndex = 0 + let decommitmentIndex = 0 + let upperBound = balancedLeafCount + params.elementCount - 1 + let lowestTreeIndex = treeIndices[params.indices.length - 1] + let nodeIndex + let nextNodeIndex + + while (true) { + nodeIndex = treeIndices[readIndex] + + if (nodeIndex === 1) { + // Given the circular nature of writeIndex, get the last writeIndex. + const rootIndex = (writeIndex === 0 ? indexCount : writeIndex) - 1 + + return { root: hashes[rootIndex], elementCount: params.elementCount } + } + + const indexIsOdd = nodeIndex & 1 + + if (nodeIndex === upperBound && !indexIsOdd) { + treeIndices[writeIndex] = nodeIndex >>> 1 + hashes[writeIndex++] = hashes[readIndex++] + } else { + const nextReadIndex = (readIndex + 1) % indexCount + nextNodeIndex = treeIndices[nextReadIndex] + + // The next node is a sibling of the current one + const nextIsPair = nextNodeIndex === nodeIndex - 1 + + const right = indexIsOdd ? hashes[readIndex++] : params.decommitments[decommitmentIndex++] + readIndex %= indexCount + const left = indexIsOdd && !nextIsPair ? params.decommitments[decommitmentIndex++] : hashes[readIndex++] + + treeIndices[writeIndex] = nodeIndex >>> 1 + hashes[writeIndex++] = options.hashFunction(left, right) + } + + readIndex %= indexCount + writeIndex %= indexCount + + if (nodeIndex === lowestTreeIndex || nextNodeIndex === lowestTreeIndex) { + lowestTreeIndex >>>= 1 + upperBound >>>= 1 + } + } +} + +// Compute the existing root given a set of leafs, their indices, and a set of decommitments +// and computes a new root, along the way, given new leafs to take their place. +// See getRoot for relevant inline comments. +export const getNewRoot = (params: getNewRootParams, options: treeOptions = defaultTreeOptions): { root: Buffer, newRoot: Buffer, elementCount: number } => { + if (params.compactProof.length > 0) { + params.elementCount = from32ByteBuffer(params.compactProof[0]) + params.decommitments = params.compactProof.slice(1) + } + + const balancedLeafCount = roundUpToPowerOf2(params.elementCount) + const hashes = params.leafs.map((leaf) => leaf).reverse() + const updateHashes = params.updateLeafs.map((leaf) => leaf).reverse() + const treeIndices = params.indices.map((index) => balancedLeafCount + index).reverse() + const indexCount = params.indices.length + + let readIndex = 0 + let writeIndex = 0 + let decommitmentIndex = 0 + let upperBound = balancedLeafCount + params.elementCount - 1 + let lowestTreeIndex = treeIndices[params.indices.length - 1] + let nodeIndex + let nextNodeIndex + + while (true) { + nodeIndex = treeIndices[readIndex] + + if (nodeIndex === 1) { + const rootIndex = (writeIndex === 0 ? indexCount : writeIndex) - 1 + + return { root: hashes[rootIndex], newRoot: updateHashes[rootIndex], elementCount: params.elementCount } + } + + const indexIsOdd = nodeIndex & 1 + + if (nodeIndex === upperBound && !indexIsOdd) { + treeIndices[writeIndex] = nodeIndex >>> 1 + hashes[writeIndex] = hashes[readIndex] + updateHashes[writeIndex++] = updateHashes[readIndex++] + } else { + const nextReadIndex = (readIndex + 1) % indexCount + nextNodeIndex = treeIndices[nextReadIndex] + const nextIsPair = nextNodeIndex === nodeIndex - 1 + + const right = indexIsOdd ? hashes[readIndex] : params.decommitments[decommitmentIndex] + const newRight = indexIsOdd ? updateHashes[readIndex++] : params.decommitments[decommitmentIndex++] + readIndex %= indexCount + const left = indexIsOdd && !nextIsPair ? params.decommitments[decommitmentIndex] : hashes[readIndex] + const newLeft = indexIsOdd && !nextIsPair ? params.decommitments[decommitmentIndex++] : updateHashes[readIndex++] + + treeIndices[writeIndex] = nodeIndex >>> 1 + hashes[writeIndex] = options.hashFunction(left, right) + updateHashes[writeIndex++] = options.hashFunction(newLeft, newRight) + } + + readIndex %= indexCount + writeIndex %= indexCount + + if (nodeIndex === lowestTreeIndex || nextNodeIndex === lowestTreeIndex) { + lowestTreeIndex >>>= 1 + upperBound >>>= 1 + } + } +} + +// This is identical to the above getRoot, except it builds a tree, similar to Common.buildTree +// See above getRoot for relevant inline comments +export const getPartialTree = (indices: Array, leafs: Array, compactProof: Array, elementCount: number, decommitments: Array, options: treeOptions = defaultTreeOptions): { tree: Array, elementCount: number } => { + if (compactProof.length > 0) { + elementCount = from32ByteBuffer(compactProof[0]) + decommitments = compactProof.slice(1) + } + + const balancedLeafCount = roundUpToPowerOf2(elementCount) + const tree = Array(balancedLeafCount << 1).fill(null) + + // Keep verification minimal by using circular hashes queue with separate read and write heads + const hashes = leafs.map((leaf) => leaf).reverse() + const treeIndices = indices.map((index) => balancedLeafCount + index).reverse() + const indexCount = indices.length + + let readIndex = 0 + let writeIndex = 0 + let decommitmentIndex = 0 + let upperBound = balancedLeafCount + elementCount - 1 + let lowestTreeIndex = treeIndices[indices.length - 1] + let nodeIndex + let nextNodeIndex + + while (true) { + nodeIndex = treeIndices[readIndex] + + if (nodeIndex === 1) { + const rootIndex = (writeIndex === 0 ? indexCount : writeIndex) - 1 + tree[1] = hashes[rootIndex] + + return { tree, elementCount } + } + + const indexIsOdd = nodeIndex & 1 + + if (nodeIndex === upperBound && !indexIsOdd) { + treeIndices[writeIndex] = nodeIndex >>> 1 + tree[nodeIndex] = hashes[readIndex] + hashes[writeIndex++] = hashes[readIndex++] + } else { + const nextReadIndex = (readIndex + 1) % indexCount + nextNodeIndex = treeIndices[nextReadIndex] + + // The next node is a sibling of the current one + const nextIsPair = nextNodeIndex === nodeIndex - 1 + + const right = indexIsOdd ? hashes[readIndex++] : decommitments[decommitmentIndex++] + readIndex %= indexCount + const left = indexIsOdd && !nextIsPair ? decommitments[decommitmentIndex++] : hashes[readIndex++] + + treeIndices[writeIndex] = nodeIndex >>> 1 + hashes[writeIndex++] = options.hashFunction(left, right) + + if (indexIsOdd) { + tree[nodeIndex] = right + tree[nodeIndex - 1] = left + } else { + tree[nodeIndex] = left + tree[nodeIndex + 1] = right + } + } + + readIndex %= indexCount + writeIndex %= indexCount + + if (nodeIndex === lowestTreeIndex || nextNodeIndex === lowestTreeIndex) { + lowestTreeIndex >>>= 1 + upperBound >>>= 1 + } + } +} \ No newline at end of file diff --git a/js/src/ts/index.ts b/js/src/ts/index.ts new file mode 100644 index 0000000..8fd7e1a --- /dev/null +++ b/js/src/ts/index.ts @@ -0,0 +1,2 @@ +export { MerkleTree } from './merkle-trees' +export { PartialMerkleTree } from './partial-merkle-trees' diff --git a/js/src/ts/merkle-trees.ts b/js/src/ts/merkle-trees.ts new file mode 100644 index 0000000..19f2797 --- /dev/null +++ b/js/src/ts/merkle-trees.ts @@ -0,0 +1,547 @@ +import { hashNode, getHashFunction, to32ByteBuffer } from './utils' +import * as Common from './common' +import * as SingleProofs from './single-proofs' +import * as MultiIndexedProofs from './index-multi-proofs' +import * as MultiFlagProofs from './flag-multi-proofs' +import * as AppendProofs from './append-proofs' +import * as CombinedProofs from './combined-proofs' +import { CommonOptions } from 'child_process' + +export class MerkleTree { + _unbalanced: boolean + _depth: number + _sortedHash: boolean + _elementPrefix: Buffer + _elements: Array + _tree: Array + + constructor(elements: Array, options?: Common.proofOptions) { + options = Object.assign({}, Common.defaultProofOptions, options) + this._elementPrefix = Buffer.from(options.elementPrefix, 'hex') + this._sortedHash = options.sortedHash + this._unbalanced = options.unbalanced + this._elements = elements.map(Buffer.from) + + if (elements.length === 0) { + this._depth = 0 + this._tree = [] + + return + } + + const balancedLeafCount = Common.getBalancedLeafCount(this._elements.length) + if (!options.unbalanced && elements.length !== balancedLeafCount) throw new Error('Incorrect element count for balanced tree.') + + const leafs = this._elements.map((element) => hashNode(this._elementPrefix, element)) + + const hashFunction = getHashFunction(this._sortedHash) + const { tree, depth } = Common.buildTree(leafs, Object.assign({}, Common.defaultTreeOptions, { + hashFunction: hashFunction, + sortedHash: options.sortedHash, + unbalanced: options.unbalanced, + })) + + this._tree = tree + this._depth = depth + this._tree[0] = MerkleTree.computeMixedRoot(this._elements.length, this._tree[1]) + } + + static verifySingleProof(proof: Common.proof, options?: Common.proofOptions): boolean { + options = Object.assign({}, Common.defaultProofOptions, options) + const prefixBuffer = Buffer.from(options.elementPrefix, 'hex') + const hashFunction = getHashFunction(options.sortedHash) + const leaf = hashNode(prefixBuffer, proof.element) + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }) + const { root: recoveredRoot, elementCount: recoveredElementCount } = SingleProofs.getRoot({ index: proof.index, leaf, compactProof: proof.compactProof, elementCount: proof.elementCount, decommitments: proof.decommitments }, opts) + + return MerkleTree.verifyMixedRoot(proof.root, recoveredElementCount, recoveredRoot) + } + + static updateWithSingleProof(proof: Common.updateProof, options?: Common.proofOptions): { root: Buffer } { + options = Object.assign({}, Common.defaultProofOptions, options) + const prefixBuffer = Buffer.from(options.elementPrefix, 'hex') + const hashFunction = getHashFunction(options.sortedHash) + const leaf = hashNode(prefixBuffer, proof.element) + const updateLeaf = hashNode(prefixBuffer, proof.updateElement) + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }) + const { root: recoveredRoot, newRoot, elementCount: recoveredElementCount } = SingleProofs.getNewRoot({ index: proof.index, leaf, updateLeaf, compactProof: proof.compactProof, elementCount: proof.elementCount, decommitments: proof.decommitments }, opts) + + if (!MerkleTree.verifyMixedRoot(proof.root, recoveredElementCount, recoveredRoot)) throw new Error('Invalid Proof.') + return { root: MerkleTree.computeMixedRoot(recoveredElementCount, newRoot) } + } + + static verifyMultiProof(proof: Common.proof, options?: Common.proofOptions): boolean { + options = Object.assign({}, Common.defaultProofOptions, options) + const prefixBuffer = Buffer.from(options.elementPrefix, 'hex') + const hashFunction = getHashFunction(options.sortedHash) + const leafs = proof.elements.map((element) => hashNode(prefixBuffer, element)) + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }) + const { root: recoveredRoot, elementCount: recoveredElementCount } = proof.indices.length > 0 + ? MultiIndexedProofs.getRoot({ indices: proof.indices, leafs, compactProof: proof.compactProof, elementCount: proof.elementCount, decommitments: proof.decommitments }, opts) + : MultiFlagProofs.getRoot({ leafs, compactProof: proof.compactProof, elementCount: proof.elementCount, flags: proof.flags, skips: proof.skips, orders: proof.orders, decommitments: proof.decommitments }, opts) + return MerkleTree.verifyMixedRoot(proof.root, recoveredElementCount, recoveredRoot) + } + + static getMultiProofIndices(proof: Common.proof): Array { + return MultiFlagProofs.getIndices(proof).indices + } + + static updateWithMultiProof(proof: Common.updateProof, options?: Common.proofOptions): { root: Buffer } { + options = Object.assign({}, Common.defaultProofOptions, options) + const prefixBuffer = Buffer.from(options.elementPrefix, 'hex') + const hashFunction = getHashFunction(options.sortedHash) + const leafs = proof.elements.map((element) => hashNode(prefixBuffer, element)) + const updateLeafs = proof.updateElements.map((element) => hashNode(prefixBuffer, element)) + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }) + const { root: recoveredRoot, newRoot, elementCount: recoveredElementCount } = proof.indices + ? MultiIndexedProofs.getNewRoot({ indices: proof.indices, leafs, updateLeafs, compactProof: proof.compactProof, elementCount: proof.elementCount, decommitments: proof.decommitments }, opts) + : MultiFlagProofs.getNewRoot({ leafs, updateLeafs, compactProof: proof.compactProof, elementCount: proof.elementCount, flags: proof.flags, skips: proof.skips, orders: proof.orders, decommitments: proof.decommitments }, opts) + + if (!MerkleTree.verifyMixedRoot(proof.root, recoveredElementCount, recoveredRoot)) throw new Error('Invalid Proof.') + + return { root: MerkleTree.computeMixedRoot(recoveredElementCount, newRoot) } + } + + static verifyAppendProof(proof: Common.proof, options?: Common.proofOptions): boolean { + options = Object.assign({}, Common.defaultProofOptions, options) + // if (!options.unbalanced) throw new Error('Append-Proofs not supported for balanced trees.') + if (proof.root.equals(to32ByteBuffer(0))) return true + + const hashFunction = getHashFunction(options.sortedHash) + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }) + const { root: recoveredRoot, elementCount: recoveredElementCount } = AppendProofs.getRoot({ compactProof: proof.compactProof, elementCount: proof.elementCount, decommitments: proof.decommitments }, opts) + + return MerkleTree.verifyMixedRoot(proof.root, recoveredElementCount, recoveredRoot) + } + + static appendWithAppendProof(proof: Common.appendProof, options?: Common.proofOptions): { root: Buffer, elementCount: number } { + options = Object.assign({}, Common.defaultProofOptions, options) + // if (!options.unbalanced) throw new Error('Append-Proofs not supported for balanced trees.') + + if (proof.root.equals(to32ByteBuffer(0))) { + const merkleTree = new MerkleTree(proof.appendElements || [proof.appendElement], options) + return { root: merkleTree.root, elementCount: proof.appendElements?.length ?? 1 } + } + + const prefixBuffer = Buffer.from(options.elementPrefix, 'hex') + + const hashFunction = getHashFunction(options.sortedHash) + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }) + const { root: recoveredRoot, newRoot, elementCount: recoveredElementCount } = proof.appendElement?.length > 0 + ? AppendProofs.getNewRoot({ appendLeaf: hashNode(prefixBuffer, proof.appendElement), compactProof: proof.compactProof, elementCount: proof.elementCount, decommitments: proof.decommitments }, opts) + : AppendProofs.getNewRoot({ appendLeafs: proof.appendElements.map((element) => hashNode(prefixBuffer, element)), compactProof: proof.compactProof, elementCount: proof.elementCount, decommitments: proof.decommitments }, opts) + const newElementCount = recoveredElementCount + (proof.appendElements?.length ?? 1) + + if (!MerkleTree.verifyMixedRoot(proof.root, recoveredElementCount, recoveredRoot)) throw new Error('Invalid Proof.') + + return { root: MerkleTree.computeMixedRoot(newElementCount, newRoot), elementCount: newElementCount } + } + + static appendWithCombinedProof(proof: Common.appendProof, options?: Common.proofOptions): { root: Buffer, elementCount: number } { + options = Object.assign({}, Common.defaultProofOptions, options) + // if (!options.unbalanced) throw new Error('Combined-Proofs not supported for balanced trees.') + + if (proof.root.equals(to32ByteBuffer(0))) { + const merkleTree = new MerkleTree(proof.appendElements || [proof.appendElement], options) + return { root: merkleTree.root, elementCount: proof.appendElements?.length ?? 1 } + } + + const prefixBuffer = Buffer.from(options.elementPrefix, 'hex') + const leaf = hashNode(prefixBuffer, proof.element) + const leafs = proof.elements.map((element) => hashNode(prefixBuffer, element)) + + const hashFunction = getHashFunction(options.sortedHash) + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }) + const { root: recoveredRoot, elementCount: recoveredElementCount, appendDecommitments } = CombinedProofs.getRoot({ leafs, leaf, index: proof.index, compactProof: proof.compactProof, elementCount: proof.elementCount, flags: proof.flags, orders: proof.orders, skips: proof.skips, decommitments: proof.decommitments }, opts) + + const newRoot = proof.appendElement instanceof Buffer + ? AppendProofs.appendSingle(hashNode(prefixBuffer, proof.appendElement), recoveredElementCount, proof.decommitments, opts) + : AppendProofs.appendMulti(proof.appendElements.map((element) => hashNode(prefixBuffer, element)), recoveredElementCount, proof.decommitments, opts) + const newElementCount = recoveredElementCount + (proof.appendElements?.length ?? 1) + + if (!MerkleTree.verifyMixedRoot(proof.root, recoveredElementCount, recoveredRoot)) throw new Error('Invalid Proof.') + + return { root: MerkleTree.computeMixedRoot(newElementCount, newRoot), elementCount: newElementCount } + } + + static verifyCombinedProof(proof: Common.proof, options?: Common.proofOptions): boolean { + options = Object.assign({}, Common.defaultProofOptions, options) + if (!options.unbalanced) throw new Error('Combined-Proofs not supported for balanced trees.') + + const prefixBuffer = Buffer.from(options.elementPrefix, 'hex') + const hashFunction = getHashFunction(options.sortedHash) + + const leaf = hashNode(prefixBuffer, proof.element) + const leafs = proof.elements.map((e) => hashNode(prefixBuffer, e)) + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }, options) + + const { root: recoveredRoot, elementCount: recoveredElementCount } = CombinedProofs.getRoot({ leafs, leaf, index: proof.index, compactProof: proof.compactProof, elementCount: proof.elementCount, flags: proof.flags, orders: proof.orders, skips: proof.skips, decommitments: proof.decommitments }, opts) + + return MerkleTree.verifyMixedRoot(proof.root, recoveredElementCount, recoveredRoot) + } + + static getCombinedProofIndices(leafCount: number, compactProof?: Array, flags?: Array<1 | 0>, skips?: Array<1 | 0>, orders?: Array<1 | 0>): Array { + return MerkleTree.getMultiProofIndices({ elementCount: leafCount, compactProof, flags, skips, orders }) + } + + static updateAndAppendWithCombinedProof(proof: Common.updateAndAppendProof, options?: Common.proofOptions): { root: Buffer, elementCount: number } { + options = Object.assign({}, Common.defaultProofOptions, options) + if (!options.unbalanced) throw new Error('Combined-Proofs not supported for balanced trees.') + + const prefixBuffer = Buffer.from(options.elementPrefix, 'hex') + const hashFunction = getHashFunction(options.sortedHash) + + const leaf = hashNode(prefixBuffer, proof.element) + const updateLeaf = hashNode(prefixBuffer, proof.updateElement) + const leafs = proof.elements.map((e) => hashNode(prefixBuffer, e)) + const updateLeafs = proof.updateElements.map((element) => hashNode(prefixBuffer, element)) + + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }, options) + const { root: recoveredRoot, elementCount: recoveredElementCount, appendDecommitments } = CombinedProofs.getNewRoot({ leafs, updateLeafs, elementCount: proof.elementCount, flags: proof.flags, skips: proof.skips, orders: proof.orders, decommitments: proof.decommitments, compactProof: proof.compactProof, index: proof.index, leaf, updateLeaf }, opts) + + const newRoot = proof.appendElements?.length > 0 + ? AppendProofs.appendMulti(proof.appendElements.map((element) => hashNode(prefixBuffer, element)), recoveredElementCount, proof.decommitments, opts) + : AppendProofs.appendSingle(hashNode(prefixBuffer, proof.appendElement), recoveredElementCount, proof.decommitments, opts) + + const appendCount = proof.appendElements?.length || 1 + const newElementCount = recoveredElementCount + appendCount + + if (!MerkleTree.verifyMixedRoot(proof.root, recoveredElementCount, recoveredRoot)) throw new Error('Invalid Proof.') + + return { + root: MerkleTree.computeMixedRoot(newElementCount, newRoot), + elementCount: newElementCount, + } + } + + static computeMixedRoot(elementCount: number, root: Buffer): Buffer { + return hashNode(to32ByteBuffer(elementCount), root) + } + + static verifyMixedRoot(mixedRoot: Buffer, elementCount: number, root: Buffer): boolean { + return MerkleTree.computeMixedRoot(elementCount, root).equals(mixedRoot) + } + + static verifySizeProof(proof: Common.proof, options?: Common.proofOptions): boolean { + options = Object.assign({}, Common.defaultProofOptions, options) + const decommitments = proof.compactProof + + if (proof.root.equals(to32ByteBuffer(0)) && proof.elementCount === 0) return true + + if (proof.element) return MerkleTree.verifyMixedRoot(proof.root, proof.elementCount, proof.element) + + if (options.sortedHash) throw new Error('Can only verify simple Size Proofs for sorted hashed trees.') + + const hashFunction = getHashFunction(options.sortedHash) + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }, options) + const { root: recoveredRoot } = AppendProofs.getRoot({ compactProof: proof.compactProof, elementCount: proof.elementCount, decommitments }, opts) + + return MerkleTree.verifyMixedRoot(proof.root, proof.elementCount, recoveredRoot) + } + + get root(): Buffer { + return this._elements.length ? Buffer.from(this._tree[0]) : to32ByteBuffer(0) + } + + get elementRoot(): Buffer { + return this._elements.length ? Buffer.from(this._tree[1]) : to32ByteBuffer(0) + } + + get depth(): number { + return this._depth + } + + get elements(): Array { + return this._elements.map(Buffer.from) + } + + get minimumCombinedProofIndex(): number { + return CombinedProofs.getMinimumIndex(this._elements.length) + } + + generateSingleProof(index: number, options?: Common.proofOptions): Common.proof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (this._elements.length <= 0) throw new Error('Tree is empty.') + if (index < 0 || index >= this._elements.length) throw new Error('Index out of range.') + + const proof = SingleProofs.generate(this._tree, this._elements.length, index, options) + const base = { root: this.root, element: Buffer.from(this._elements[index]) } + + return Object.assign(base, proof) + } + + generateSingleUpdateProof(index: number, updateElement: Buffer, options?: Common.proofOptions): Common.updateProof { + options = Object.assign({}, Common.defaultProofOptions, options) + const base = { updateElement: Buffer.from(updateElement) } + + return Object.assign(base, this.generateSingleProof(index, options)) + } + + updateSingle(index: number, updateElement: Buffer, options?: Common.proofOptions): { proof: Common.updateProof, newMerkleTree: MerkleTree } { + options = Object.assign({}, Common.defaultProofOptions, options) + const newElements = this._elements.map((e, i) => (i === index ? updateElement : e)) + + const opts = Object.assign({}, Common.defaultTreeOptions, { + sortedHash: this._sortedHash, + unbalanced: this._unbalanced, + elementPrefix: this._elementPrefix, + }, options) + + return { + proof: this.generateSingleUpdateProof(index, updateElement, options), + newMerkleTree: new MerkleTree(newElements, opts), + } + } + + generateMultiProof(indices: Array, options?: Common.proofOptions): Common.proof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (this._elements.length <= 0) throw new Error('Tree is empty.') + indices.forEach((index, i) => { + if (index < 0 || index > this._elements.length) throw new Error('Index out of range.') + if (indices.indexOf(index) !== i) throw new Error('Duplicate in indices.') + }) + + const opts = Object.assign({ unbalanced: this._unbalanced, sortedHash: this._sortedHash }, options) + + const proof = options.indexed + ? MultiIndexedProofs.generate(this._tree, this.elements.length, indices, opts) + : MultiFlagProofs.generate(this._tree, this.elements.length, indices, opts) + + const elements = indices.map((index) => Buffer.from(this._elements[index])) + const base = { root: this.root, elements } + + return Object.assign(base, proof) + } + + generateMultiUpdateProof(indices: Array, updateElements: Array, options?: Common.proofOptions): Common.updateProof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (indices.length !== updateElements.length) throw new Error('Indices and element count mismatch.') + const base = { updateElements: updateElements.map(Buffer.from) } + + return Object.assign(base, this.generateMultiProof(indices, options)) + } + + updateMulti(indices: Array, updateElements: Array, options?: Common.proofOptions): { proof: Common.updateProof, newMerkleTree: MerkleTree } { + options = Object.assign({}, Common.defaultProofOptions, options) + const newElements = this.elements.map((e, i) => { + const index = indices.indexOf(i) + + return index >= 0 ? updateElements[index] : e + }) + + const opts = Object.assign({}, Common.defaultTreeOptions, { + sortedHash: this._sortedHash, + unbalanced: this._unbalanced, + elementPrefix: this._elementPrefix, + }, options) + + return { + proof: this.generateMultiUpdateProof(indices, updateElements, options), + newMerkleTree: new MerkleTree(newElements, opts), + } + } + + generateAppendProof(options?: Common.proofOptions): Common.proof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (!this._unbalanced) throw new Error('Can only generate Append-Proofs for unbalanced trees.') + if (this._elements.length === 0) { + return options.compact + ? { root: this.root, compactProof: [to32ByteBuffer(0)] } + : { root: this.root, elementCount: 0, decommitments: [] } + } + + const proof = AppendProofs.generate(this._tree, this._elements.length, options) + return Object.assign({ root: this.root }, proof) + } + + generateSingleAppendProof(appendElement: Buffer, options?: Common.proofOptions): Common.appendProof { + options = Object.assign({}, Common.defaultProofOptions, options) + const base = { appendElement: Buffer.from(appendElement) } + + return Object.assign(base, this.generateAppendProof(options)) + } + + generateMultiAppendProof(appendElements: Array, options?: Common.proofOptions): Common.appendProof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (appendElements.length <= 0) throw new Error('No elements provided.') + const base = { appendElements: appendElements.map(Buffer.from) } + + return Object.assign(base, this.generateAppendProof(options)) + } + + appendSingle(appendElement: Buffer, options?: Common.proofOptions): { proof: Common.appendProof, newMerkleTree: MerkleTree } { + options = Object.assign({}, Common.defaultProofOptions, options) + const newElements = this.elements.map((e) => e) + newElements.push(appendElement) + + const opts = Object.assign({}, Common.defaultTreeOptions, { + sortedHash: this._sortedHash, + unbalanced: this._unbalanced, + elementPrefix: this._elementPrefix, + }, options) + return { + proof: this.generateSingleAppendProof(appendElement, options), + newMerkleTree: new MerkleTree(newElements, opts), + } + } + + appendMulti(appendElements: Array, options?: Common.proofOptions): { proof: Common.appendProof, newMerkleTree: MerkleTree } { + options = Object.assign({}, Common.defaultProofOptions, options) + const newElements = this.elements.concat(appendElements) + + const opts = Object.assign({}, Common.defaultTreeOptions, { + sortedHash: this._sortedHash, + unbalanced: this._unbalanced, + elementPrefix: this._elementPrefix, + }, options) + + return { + proof: this.generateMultiAppendProof(appendElements, options), + newMerkleTree: new MerkleTree(newElements, opts), + } + } + + // Todo: update the default options + // Todo: Generalize error constants + generateCombinedProof(indices: Array | number, options?: Common.proofOptions): Common.proof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (this._elements.length <= 0) throw new Error('Tree is empty.') + + if (options.indexed) throw new Error('Indexed Combined-Proofs are not yet supported.') + if (!this._unbalanced) throw new Error('Can only generate Combined-Proofs for unbalanced trees.') + + const elementCount = this._elements.length + const minimumIndex = CombinedProofs.getMinimumIndex(elementCount) + const params = { tree: this._tree, elementCount } + + let proof: Common.proof + if (Array.isArray(indices)) { + indices.forEach((index, i) => { + if (index >= this._elements.length) throw new Error('Index out of range.') + if (indices.indexOf(index) !== i) throw new Error('Duplicate in indices.') + }) + + if (indices[indices.length - 1] >= minimumIndex, `Last index must be larger than ${minimumIndex}.`) + Object.assign(params, { indices }) + proof = CombinedProofs.generate(this._tree, this._elements.length, indices, null, options) + } else { + if (indices >= this._elements.length) throw new Error('Index out of range.') + if (indices < minimumIndex) throw new Error(`Index must be larger than ${minimumIndex}.`) + Object.assign(params, { index: indices }) + proof = CombinedProofs.generate(this._tree, this._elements.length, null, indices, options) + } + + const base = { root: this.root } + + if (Array.isArray(indices)) { + const elements = indices.map((index) => Buffer.from(this._elements[index])) + Object.assign(base, { elements }) + } else { + const element = Buffer.from(this._elements[indices]) + Object.assign(base, { element }) + } + + return Object.assign(base, proof) + } + + generateUpdateAppendProof(proof: Common.updateAndAppendProof, options?: Common.proofOptions): Common.updateAndAppendProof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (proof.indices.length > 0 && proof.updateElements.length <= 0) throw new Error('Indices and update mismatch.') + if (proof.index == null && proof.indices.length <= 0) throw new Error('No elements provided to be proven') + if (proof.index == null && proof.indices.length !== proof.updateElements.length) throw new Error('Indices and update element count mismatch.') + if (proof.appendElements.length <= 0) throw new Error('No elements provided to be appended.') + + const base = {} + proof.updateElements.length > 0 + ? Object.assign(base, { updateElements: proof.updateElements.map(Buffer.from) }) + : Object.assign(base, { updateElement: Buffer.from(proof.updateElement) }) + + proof.appendElements.length > 0 + ? Object.assign(base, { appendElements: proof.appendElements.map(Buffer.from) }) + : Object.assign(base, { appendElement: Buffer.from(proof.appendElement) }) + + return Object.assign(base, this.generateCombinedProof(proof.indices, options)) + } + + generateUseAppendProof(proof: Common.appendProof, options?: Common.proofOptions): Common.appendProof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (!Number.isInteger(proof.indices) && proof.indices.length <= 0) throw new Error('No elements provided to be proven') + if (Array.isArray(proof.appendElements) && proof.appendElements.length <= 0) throw new Error('No elements provided to be appended.') + + const base = {} + proof.appendElements.length > 0 + ? Object.assign(base, { appendElements: proof.appendElements.map(Buffer.from) }) + : Object.assign(base, { appendElement: Buffer.from(proof.appendElement) }) + + return Object.assign(base, this.generateCombinedProof(proof.indices, options)) + } + + updateAndAppend(proof: Common.updateAndAppendProof, options?: Common.proofOptions): { proof: Common.updateAndAppendProof, newMerkleTree: MerkleTree } { + options = Object.assign({}, Common.defaultProofOptions, options) + const { newMerkleTree: updatedTree } = proof.updateElements?.length > 0 + ? this.updateMulti(proof.indices, proof.updateElements, options) + : this.updateSingle(proof.index, proof.updateElement, options) + + const { newMerkleTree } = proof.appendElements?.length > 0 + ? updatedTree.appendMulti(proof.appendElements, options) + : updatedTree.appendSingle(proof.appendElement, options) + + return { + proof: this.generateUpdateAppendProof({ indices: proof.indices, updateElements: proof.updateElements, appendElements: proof.appendElements }, options), + newMerkleTree, + } + } + + useAndAppend(indices: Array | number, appendElements: Array | Buffer, options?: Common.proofOptions): { proof: Common.appendProof, newMerkleTree: MerkleTree } { + options = Object.assign({}, Common.defaultProofOptions, options) + let proof: Common.appendProof + if (!Array.isArray(indices)) { + proof.index = indices + } else { + proof.indices = indices + } + + if (!Array.isArray(appendElements)) { + proof.appendElement = appendElements + } else { + proof.appendElements = appendElements + } + + const { newMerkleTree } = Array.isArray(appendElements) + ? this.appendMulti(appendElements, options) + : this.appendSingle(proof.appendElement, options) + + return { + proof: this.generateUseAppendProof(proof, options), + newMerkleTree, + } + } + + generateSizeProof(options?: Common.proofOptions): Common.proof { + options = Object.assign({}, Common.defaultProofOptions, options) + const elementCount = this._elements.length + + if (elementCount === 0) { + const root = to32ByteBuffer(0) + const element = to32ByteBuffer(0) + + if (options.simple) return { root, elementCount, element } + + if (this._sortedHash) throw new Error('Can only generate simple Size Proofs for sorted hashed trees.') + + return options.compact ? { root, elementCount, compactProof: [] } : { root, elementCount, decommitments: [] } + } + + if (options.simple) return { root: this.root, elementCount, element: this.elementRoot } + + if (this._sortedHash) throw new Error('Can only generate simple Size Proofs for sorted hashed trees.') + + const opts = Object.assign({}, options, { compact: false }) + const proof = AppendProofs.generate(this._tree, elementCount, opts) + const decommitments = proof.decommitments + + if (options.compact) return { root: this.root, elementCount, compactProof: decommitments } + + return { root: this.root, elementCount, decommitments } + } +} diff --git a/js/src/ts/partial-merkle-trees.ts b/js/src/ts/partial-merkle-trees.ts new file mode 100644 index 0000000..ef5c721 --- /dev/null +++ b/js/src/ts/partial-merkle-trees.ts @@ -0,0 +1,346 @@ +import { hashNode, getHashFunction, to32ByteBuffer, from32ByteBuffer } from './utils' +import { MerkleTree } from './merkle-trees' +import * as Common from './common' +import * as SingleProofs from './single-proofs' +import * as MultiIndexedProofs from './index-multi-proofs' + +export class PartialMerkleTree extends MerkleTree { + _elements: Array + _tree: Array + _depth: number + + constructor(elements: Array, tree: Array, options?: Common.proofOptions) { + options = Object.assign({}, Common.defaultProofOptions, options) + if (tree.length <= 1) throw new Error('Cannot create empty Partial Tree.') + if (tree.length >> 1 !== Common.getBalancedLeafCount(elements.length)) throw new Error('Element and tree mismatch.') + + super([], options) + this._elements = elements.map((e) => e && Buffer.from(e)) + this._tree = tree.map((n) => n && Buffer.from(n)) + this._depth = Common.getDepth(elements.length) + this._tree[0] = MerkleTree.computeMixedRoot(elements.length, tree[1]) + } + + static fromSingleProof(proof: Common.proof, options?: Common.proofOptions): PartialMerkleTree { + options = Object.assign({}, Common.defaultProofOptions, options) + const prefixBuffer = Buffer.from(options.elementPrefix, 'hex') + const hashFunction = getHashFunction(options.sortedHash) + const leaf = hashNode(prefixBuffer, proof.element) + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }, options) + const { tree, elementCount: recoveredElementCount } = SingleProofs.getPartialTree(proof.index, leaf, proof.compactProof, proof.elementCount, proof.decommitments, opts) + + const partialElements = Array(recoveredElementCount).fill(null) + partialElements[proof.index] = proof.element + + return new PartialMerkleTree(partialElements, tree, options) + } + + static fromSingleUpdateProof(proof: Common.updateProof, options?: Common.proofOptions): PartialMerkleTree { + options = Object.assign({}, Common.defaultProofOptions, options) + return PartialMerkleTree.fromSingleProof({ index: proof.index, element: proof.updateElement, elementCount: proof.elementCount, compactProof: proof.compactProof, decommitments: proof.decommitments }, options) + } + + static fromMultiProof(proof: Common.proof, options?: Common.proofOptions): PartialMerkleTree { + options = Object.assign({}, Common.defaultProofOptions, options) + if (proof.indices.length <= 0 && options.sortedHash) throw new Error('Cannot build sorted-hash Partial Tree from existence-only multi-proof.') + + proof.indices = proof.indices ?? super.getMultiProofIndices({ elementCount: proof.elementCount, compactProof: proof.compactProof, flags: proof.flags, skips: proof.skips, orders: proof.orders }) + proof.compactProof = !proof.indices && proof.compactProof + ? [proof.compactProof[0]].concat(proof.compactProof.slice(4)) + : proof.compactProof + + const prefixBuffer = Buffer.from(options.elementPrefix, 'hex') + const hashFunction = getHashFunction(options.sortedHash) + const leafs = proof.elements.map((element) => hashNode(prefixBuffer, element)) + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }, options) + const { tree, elementCount: recoveredElementCount } = MultiIndexedProofs.getPartialTree(proof.indices, leafs, proof.compactProof, proof.elementCount, proof.decommitments, opts) + + const partialElements = Array(recoveredElementCount).fill(null) + proof.indices.forEach((index, i) => (partialElements[index] = proof.elements[i])) + + return new PartialMerkleTree(partialElements, tree, options) + } + + static fromMultiUpdateProof(proof: Common.updateProof, options?: Common.proofOptions): PartialMerkleTree { + options = Object.assign({}, Common.defaultProofOptions, options) + return PartialMerkleTree.fromMultiProof({ indices: proof.indices, elements: proof.updateElements, elementCount: proof.elementCount, compactProof: proof.compactProof, decommitments: proof.decommitments, flags: proof.flags, skips: proof.skips, orders: proof.orders }, options) + } + + static fromAppendProof(proof: Common.appendProof, options?: Common.proofOptions): PartialMerkleTree { + options = Object.assign({}, Common.defaultProofOptions, options) + if (!proof.appendElement && !proof.appendElements) throw new Error('Append elements required.') + + const index = proof.elementCount ?? from32ByteBuffer(proof.compactProof[0]) + const element = proof.appendElement ?? proof.appendElements[0] + + const prefixBuffer = Buffer.from(options.elementPrefix, 'hex') + const hashFunction = getHashFunction(options.sortedHash) + const leaf = hashNode(prefixBuffer, element) + + if (proof.compactProof) { + proof.compactProof[0] = to32ByteBuffer(index + 1) + } + + const opts = Object.assign({}, Common.defaultTreeOptions, { hashFunction }, options) + const { tree } = SingleProofs.getPartialTree(index, leaf, proof.compactProof, proof.elementCount, proof.decommitments, opts) + + const partialElements = Array(index) + .fill(null) + .concat(proof.appendElement ?? proof.appendElements) + + const leafs = partialElements.map((element) => element && hashNode(prefixBuffer, element)) + const newTree = Common.getGrownTree(tree, leafs, opts) + + return new PartialMerkleTree(partialElements, newTree, options) + } + + // TODO: from combined proofs + + get elements(): Array { + return this._elements.map((e) => e && Buffer.from(e)) + } + + generateSingleProof(index: number, options?: Common.proofOptions): Common.proof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (this._elements.length <= 0) throw new Error('Tree has no known elements.') + if (index < 0 || index >= this._elements.length) throw new Error('Index out of range.') + if (!this._elements[index]) throw new Error('Partial tree does not have element.') + + return super.generateSingleProof(index, options) + } + + generateSingleUpdateProof(index: number, updateElement: Buffer, options?: Common.proofOptions): Common.updateProof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (this._elements.length <= 0) throw new Error('Tree has no known elements.') + if (index < 0 || index >= this._elements.length) throw new Error('Index out of range.') + if (!this._elements[index]) throw new Error('Partial tree does not have element.') + + return super.generateSingleUpdateProof(index, updateElement, options) + } + + updatePartialSingle(index: number, updateElement: Buffer, options?: Common.proofOptions): { proof: Common.updateProof, newPartialTree: PartialMerkleTree } { + options = Object.assign({}, Common.defaultProofOptions, options) + return { + proof: this.generateSingleUpdateProof(index, updateElement, options), + newPartialTree: this.set(index, updateElement), + } + } + + generateMultiProof(indices: Array, options?: Common.proofOptions): Common.proof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (this._elements.length <= 0) throw new Error('Tree has no known elements.') + + indices.forEach((index) => { + if (index < 0 || index >= this._elements.length) throw new Error('Index out of range.') + if (!this._elements[index]) throw new Error('Partial tree does not have element.') + }) + + return super.generateMultiProof(indices, options) + } + + generateMultiUpdateProof(indices: Array, updateElements: Array, options?: Common.proofOptions): Common.updateProof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (this._elements.length <= 0) throw new Error('Tree has no known elements.') + + indices.forEach((index) => { + if (index < 0 || index >= this._elements.length) throw new Error('Index out of range.') + if (!this._elements[index]) throw new Error('Partial tree does not have element.') + }) + + return super.generateMultiUpdateProof(indices, updateElements, options) + } + + updatePartialMulti(indices: Array, updateElements: Array, options?: Common.proofOptions): { proof: Common.updateProof, newPartialTree: PartialMerkleTree } { + options = Object.assign({}, Common.defaultProofOptions, options) + return { + proof: this.generateMultiUpdateProof(indices, updateElements, options), + newPartialTree: this.set(indices, updateElements), + } + } + + generateAppendProof(options?: Common.proofOptions): Common.proof { + options = Object.assign({}, Common.defaultProofOptions, options) + return super.generateAppendProof(options) + } + + generateSingleAppendProof(appendElement: Buffer, options?: Common.proofOptions): Common.appendProof { + options = Object.assign({}, Common.defaultProofOptions, options) + return super.generateSingleAppendProof(appendElement, options) + } + + generateMultiAppendProof(appendElements: Array, options?: Common.proofOptions): Common.appendProof { + options = Object.assign({}, Common.defaultProofOptions, options) + return super.generateMultiAppendProof(appendElements, options) + } + + appendPartialSingle(appendElement: Buffer, options?: Common.proofOptions): { proof: Common.appendProof, newPartialTree: PartialMerkleTree } { + options = Object.assign({}, Common.defaultProofOptions, options) + return { + proof: this.generateSingleAppendProof(appendElement, options), + newPartialTree: this.append(appendElement), + } + } + + appendPartialMulti(appendElements: Array, options?: Common.proofOptions): { proof: Common.appendProof, newPartialTree: PartialMerkleTree } { + options = Object.assign({}, Common.defaultProofOptions, options) + return { + proof: this.generateMultiAppendProof(appendElements, options), + newPartialTree: this.append(appendElements), + } + } + + generateCombinedProof(indices: Array, options?: Common.proofOptions): Common.proof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (this._elements.length < 0) throw new Error('Tree has no known elements.') + + if (!Array.isArray(indices)) { + if (indices < 0 || indices > this._elements.length) throw new Error('Index out of range.') + if (!this._elements[indices]) throw new Error('Partial tree does not have element.') + } else { + indices.forEach((index) => { + if (index < 0 || index >= this._elements.length) throw new Error('Index out of range.') + if (!this._elements[index]) throw new Error('Partial tree does not have element.') + }) + } + + return super.generateCombinedProof(indices, options) + } + + generatePartialUpdateAppendProof(indices: Array, updateElements: Array, appendElements: Array, options?: Common.proofOptions): Common.updateAndAppendProof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (this._elements.length <= 0) throw new Error('Tree has no known elements.') + + if (!Array.isArray(indices)) { + if (indices < 0 || indices >= this._elements.length) throw new Error('Index out of range.') + if (!this._elements[indices]) throw new Error('Partial tree does not have element.') + } else { + indices.forEach((index) => { + if (index < 0 || index >= this._elements.length) throw new Error('Index out of range.') + if (!this._elements[index]) throw new Error('Partial tree does not have element.') + }) + } + + return super.generateUpdateAppendProof({ indices, updateElements, appendElements }, options) + } + + generatePartialUseAppendProof(indices: Array, appendElements: Array, options?: Common.proofOptions): Common.appendProof { + options = Object.assign({}, Common.defaultProofOptions, options) + if (this._elements.length <= 0) throw new Error('Tree has no known elements.') + + if (!Array.isArray(indices)) { + if (indices < 0 || indices >= this._elements.length) throw new Error('Index out of range.') + if (!this._elements[indices]) throw new Error('Partial tree does not have element.') + } else { + indices.forEach((index) => { + if (index < 0 || index >= this._elements.length) throw new Error('Index out of range.') + if (!this._elements[index]) throw new Error('Partial tree does not have element.') + }) + } + + return super.generateUseAppendProof({ indices, appendElements }, options) + } + + updatePartialAndAppend(indices: Array, updateElements: Array, appendElements: Array, options?: Common.proofOptions): { proof: Common.updateAndAppendProof, newPartialTree: PartialMerkleTree } { + options = Object.assign({}, Common.defaultProofOptions, options) + return { + proof: this.generateUpdateAppendProof({ indices, updateElements, appendElements }, options), + newPartialTree: this.set(indices, updateElements).append(appendElements), + } + } + + usePartialAndAppend(indices: Array, appendElements: Array, options?: Common.proofOptions): { proof: Common.appendProof, newPartialTree: PartialMerkleTree } { + options = Object.assign({}, Common.defaultProofOptions, options) + return { + proof: this.generateUseAppendProof({ indices, appendElements }, options), + newPartialTree: this.append(appendElements), + } + } + + generateSizeProof(options?: Common.proofOptions): Common.proof { + options = Object.assign({}, Common.defaultProofOptions, options) + const { simple = true } = options + if (!simple && this._elements.length <= 0) throw new Error('Tree has no known elements.') + return super.generateSizeProof(options) + } + + has(indices: Array): boolean { + if (this._elements.length <= 0) throw new Error('Tree has no known elements.') + if (!Array.isArray(indices)) return this.has([indices]) + + return indices.reduce((haveAll: boolean, index) => { + if (index < 0 || index >= this._elements.length) throw new Error('Index out of range.') + return haveAll && this._elements[index] !== null + }, true) + } + + check(indices: Array | number, elements: Array | Buffer): Array { + if (this._elements.length <= 0) throw new Error('Tree has no known elements.') + + if (!Array.isArray(indices) && !Array.isArray(elements)) return this.check([indices], [elements]) + else if (!Array.isArray(elements)) return this.check(indices, [elements]) + else if (!Array.isArray(indices)) return this.check([indices], elements) + + indices.forEach((index) => { + if (index < 0 || index >= this._elements.length) throw new Error('Index out of range.') + }) + + const leafs = elements.map((element) => hashNode(this._elementPrefix, element)) + + return Common.checkElements(this._tree, indices, leafs) + } + + set(indices: Array | number, elements: Array | Buffer): PartialMerkleTree { + if (this._elements.length <= 0) throw new Error('Tree has no known elements.') + + if (!Array.isArray(indices) && !Array.isArray(elements)) return this.set([indices], [elements]) + Array.isArray(indices) && indices.forEach((index) => { + if (index < 0 || index >= this._elements.length) throw new Error('Index out of range.') + }) + + const newElements = this.elements + const elementArray = Array.isArray(elements) ? elements : Array() + Array.isArray(indices) && indices.forEach((index, i) => { + newElements[index] = elementArray[i] + }) + + const leafs = newElements.map((element) => element && hashNode(this._elementPrefix, element)) + const hashFunction = getHashFunction(this._sortedHash) + const treeOpts = Object.assign({}, Common.defaultTreeOptions, { + sortedHash: this._sortedHash, + unbalanced: this._unbalanced, + hashFunction: hashFunction + }) + const newTree = Common.getUpdatedTree(this._tree, leafs, treeOpts) + + const proofOpts = Object.assign({}, Common.defaultProofOptions, { + sortedHash: this._sortedHash, + unbalanced: this._unbalanced, + elementPrefix: this._elementPrefix, + }) + return new PartialMerkleTree(newElements, newTree, proofOpts) + } + + append(elements: Array | Buffer): PartialMerkleTree { + if (!Array.isArray(elements)) return this.append([elements]) + + const elementArray = Array.isArray(elements) ? elements : Array() + const newElements = this.elements.concat(elementArray) + const leafs = newElements.map((element) => element && hashNode(this._elementPrefix, element)) + const hashFunction = getHashFunction(this._sortedHash) + + const treeOpts = Object.assign({}, Common.defaultTreeOptions, { + sortedHash: this._sortedHash, + unbalanced: this._unbalanced, + hashFunction: hashFunction + }) + const newTree = Common.getGrownTree(this._tree, leafs, treeOpts) + + const proofOpts = Object.assign({}, Common.defaultProofOptions, { + sortedHash: this._sortedHash, + unbalanced: this._unbalanced, + elementPrefix: this._elementPrefix, + }) + return new PartialMerkleTree(newElements, newTree, proofOpts) + } +} + diff --git a/js/src/ts/single-proofs.ts b/js/src/ts/single-proofs.ts new file mode 100644 index 0000000..e4bd37e --- /dev/null +++ b/js/src/ts/single-proofs.ts @@ -0,0 +1,132 @@ +import { defaultProofOptions, defaultTreeOptions, getBalancedLeafCount, getNewRootParams, getRootParams, proof, proofOptions, treeOptions } from './common' +import { to32ByteBuffer, from32ByteBuffer } from './utils' + +// Generates a set of decommitments to prove the existence of a leaf at a given index. +export const generate = (tree: Array, elementCount: number, index: number, options: proofOptions = defaultProofOptions): proof => { + const decommitments = [] + const leafCount = tree.length >>> 1 + + for (let i = leafCount + index; i > 1; i >>>= 1) { + decommitments.unshift(i & 1 ? tree[i - 1] : tree[i + 1]) + } + + // Filter out non-existent decommitments, which are nodes to the "right" of the last leaf + const filteredDecommitments = decommitments.filter((d) => d).map(Buffer.from) + + return options.compact + ? { + index, + elementCount: 0, + compactProof: [to32ByteBuffer(elementCount)].concat(filteredDecommitments), + decommitments: Array(), + } + : { + index, + compactProof: Array(), + elementCount, + decommitments: filteredDecommitments, + } +} + +// Compute the root given a leaf, its index, and a set of decommitments. +export const getRoot = (params: getRootParams, options: treeOptions = defaultTreeOptions): { root: Buffer, elementCount: number } => { + if (params.compactProof.length > 0) { + params.elementCount = from32ByteBuffer(params.compactProof[0]) + params.decommitments = params.compactProof.slice(1) + } + + let decommitmentIndex = params.decommitments.length + let hash = Buffer.from(params.leaf) + let upperBound = params.elementCount - 1 + + while (decommitmentIndex > 0) { + // If not "right-most" node at this level, or odd, compute the parent hash + if (params.index !== upperBound || params.index & 1) { + // Note that hash order is irrelevant if hash function sorts nodes + hash = + params.index & 1 + ? options.hashFunction(params.decommitments[--decommitmentIndex], hash) + : options.hashFunction(hash, params.decommitments[--decommitmentIndex]) + } + + params.index >>>= 1 + upperBound >>>= 1 + } + + return { root: hash, elementCount: params.elementCount } +} + +// Compute the existing root given a leaf, its index, and a set of decommitments +// and computes a new root, along the way, given a new leaf to take its place. +// See getRoot for relevant inline comments. +export const getNewRoot = (params: getNewRootParams, options: treeOptions = defaultTreeOptions): { root: Buffer, newRoot: Buffer, elementCount: number } => { + if (params.compactProof.length > 0) { + params.elementCount = from32ByteBuffer(params.compactProof[0]) + params.decommitments = params.compactProof.slice(1) + } + + let decommitmentIndex = params.decommitments.length + let hash = Buffer.from(params.leaf) + let updateHash = Buffer.from(params.updateLeaf) + let upperBound = params.elementCount - 1 + + while (decommitmentIndex > 0) { + if (params.index !== upperBound || params.index & 1) { + hash = + params.index & 1 + ? options.hashFunction(params.decommitments[--decommitmentIndex], hash) + : options.hashFunction(hash, params.decommitments[--decommitmentIndex]) + + updateHash = + params.index & 1 + ? options.hashFunction(params.decommitments[decommitmentIndex], updateHash) + : options.hashFunction(updateHash, params.decommitments[decommitmentIndex]) + } + + params.index >>>= 1 + upperBound >>>= 1 + } + + return { root: hash, newRoot: updateHash, elementCount: params.elementCount } +} + +// This is identical to the above getRoot, except it builds a tree, similar to Common.buildTree +// See above getRoot for relevant inline comments +export const getPartialTree = (index: number, leaf: Buffer, compactProof: Array, elementCount: number, decommitments: Array, options: treeOptions = defaultTreeOptions): { tree: Array, elementCount: number } => { + if (compactProof.length > 0) { + elementCount = from32ByteBuffer(compactProof[0]) + decommitments = compactProof.slice(1) + } + + let balancedLeafCount = getBalancedLeafCount(elementCount) + const tree = Array(balancedLeafCount << 1).fill(null) + + let decommitmentIndex = decommitments.length + let hash = Buffer.from(leaf) + let upperBound = elementCount - 1 + + while (decommitmentIndex > 0) { + const nodeIndex = balancedLeafCount + index + tree[nodeIndex] = hash + + if (index !== upperBound || index & 1) { + hash = + index & 1 + ? options.hashFunction(decommitments[--decommitmentIndex], hash) + : options.hashFunction(hash, decommitments[--decommitmentIndex]) + + const pairIndex = index & 1 ? nodeIndex - 1 : nodeIndex + 1 + tree[pairIndex] = decommitments[decommitmentIndex] + + // maybe if (index + 1 === upperBound) we know the next decommitment is the last leaf + } + + balancedLeafCount >>>= 1 + index >>>= 1 + upperBound >>>= 1 + } + + tree[1] = hash + + return { tree, elementCount } +} diff --git a/js/src/ts/tests/helpers/index.ts b/js/src/ts/tests/helpers/index.ts new file mode 100644 index 0000000..d4a292a --- /dev/null +++ b/js/src/ts/tests/helpers/index.ts @@ -0,0 +1,53 @@ +import * as crypto from 'crypto' +import { hashNode, to32ByteBuffer } from '../../utils' + +export const generateRandomElement = (): Buffer => { + return crypto.randomBytes(32) +} + +export interface elementOptions { + seed?: string, + random?: boolean, + size?: number +} + +export const defaultElementOptions = { + seed: "random", + random: false, + size: 32, +} + +export const generateElements = (elementCount: number, options?: elementOptions): Array => { + const { seed, random = false, size = 32 } = Object.assign(defaultElementOptions, options) + const elements = [] + let seedBuffer = seed ? Buffer.from(seed, 'hex') : null + let element = seedBuffer + + for (let i = 0; i < elementCount; i++) { + element = random ? generateRandomElement() : seed ? hashNode(seedBuffer, element) : to32ByteBuffer(i) + + const elementSize = size === 0 ? element.readUInt8(31) : size + + if (element.length < elementSize) { + element = Buffer.concat([element, Buffer.alloc(elementSize - element.length)]) + } else if (element.length > elementSize) { + element = element.slice(0, elementSize) + } + + seedBuffer = seed ? element : seedBuffer + elements.push(element) + } + + return elements +} + +export const shuffle = (array: Array) => { + for (let i = array.length - 1; i > 0; i--) { + let j: number = Math.floor(Math.random() * (i + 1)) + let t: any = array[i] + array[i] = array[j] + array[j] = t + } + + return array +} diff --git a/js/src/ts/tests/helpers/merkle-tree-helpers.ts b/js/src/ts/tests/helpers/merkle-tree-helpers.ts new file mode 100644 index 0000000..9d335a0 --- /dev/null +++ b/js/src/ts/tests/helpers/merkle-tree-helpers.ts @@ -0,0 +1,377 @@ +import { expect } from 'chai' +import { defaultElementOptions, elementOptions, generateElements } from './index' +import { MerkleTree } from '../../index' +import { defaultProofOptions, defaultTreeOptions, proofOptions, treeOptions } from '../../common' + +export interface expectedTree { + root?: string, + elementRoot?: string, + depth?: number +} + +export const testBuildTree = (elementCount: number, seed: string, expected: expectedTree, treeOptions: proofOptions = defaultProofOptions, elementOptions: elementOptions = defaultElementOptions) => { + const elements = generateElements(elementCount, { seed, size: elementOptions.size }) + const merkleTree = new MerkleTree(elements, treeOptions) + + expect(merkleTree.root.toString('hex')).to.equal(expected.root) + expect(merkleTree.elementRoot.toString('hex')).to.equal(expected.elementRoot) + expect(merkleTree.depth).to.equal(expected.depth) + merkleTree.elements.forEach((e, i) => expect(e.equals(elements[i])).to.be.true) + expect(merkleTree.elements.length).to.equal(elements.length) +} + +export const compareTrees = (elementCount: number, optionsA: proofOptions, optionsB: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const treeA = new MerkleTree(elements, optionsA) + const treeB = new MerkleTree(elements, optionsB) + + expect(treeA.root.equals(treeB.root)).to.be.true + expect(treeA.elementRoot.equals(treeB.elementRoot)).to.be.true + expect(treeA.depth).to.equal(treeB.depth) + treeA.elements.forEach((e, i) => expect(e.equals(treeB.elements[i])).to.be.true) + expect(treeA.elements.length).to.equal(treeB.elements.length) +} + +export const testSingleUpdate = (elementCount: number, index: number, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const updateElement = generateElements(1, { seed: '11' })[0] + const { newMerkleTree, proof } = merkleTree.updateSingle(index, updateElement, options) + const { root } = MerkleTree.updateWithSingleProof(proof, options) + const newElements = elements.map((e, i) => (i === index ? updateElement : e)) + const freshMerkleTree = new MerkleTree(newElements, options) + + expect(root.equals(newMerkleTree.root)).to.be.true + expect(root.equals(freshMerkleTree.root)).to.be.true +} + +export const testConsecutiveSingleUpdate = (iterations: number, elementCount: number, options: proofOptions = defaultProofOptions, elementOptions: elementOptions = defaultElementOptions) => { + let elements = generateElements(elementCount, { seed: 'cc', size: elementOptions.size }) + let merkleTree = new MerkleTree(elements, options) + let root = null + + for (let i = 0; i < iterations; i++) { + const index = Math.floor(Math.random() * elementCount) + const updateElement = generateElements(1, { random: true, size: elementOptions.size })[0] + const { newMerkleTree, proof } = merkleTree.updateSingle(index, updateElement, options) + merkleTree = newMerkleTree + root = MerkleTree.updateWithSingleProof(proof, options).root + elements[index] = updateElement + + expect(root.equals(merkleTree.root)).to.be.true + } + + const finalMerkleTree = new MerkleTree(elements, options) + + expect(root.equals(finalMerkleTree.root)).to.be.true +} + +export const testMultiUpdate = (elementCount: number, indices: Array, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const updateElements = generateElements(indices.length, { seed: '11' }) + const { newMerkleTree, proof } = merkleTree.updateMulti(indices, updateElements, options) + const { root } = MerkleTree.updateWithMultiProof(proof, options) + + const newTreeElements = elements.map((e, i) => { + const index = indices.indexOf(i) + + return index >= 0 ? updateElements[index] : e + }) + + const freshMerkleTree = new MerkleTree(newTreeElements, options) + + expect(root.equals(newMerkleTree.root)).to.be.true + expect(root.equals(freshMerkleTree.root)).to.be.true +} + +export const testConsecutiveMultiUpdate = (iterations: number, elementCount: number, updateSize: number, options: proofOptions = defaultProofOptions, elementOptions: elementOptions = defaultElementOptions) => { + let elements = generateElements(elementCount, { seed: 'cc', size: elementOptions.size }) + let merkleTree = new MerkleTree(elements, options) + let root = null + + for (let i = 0; i < iterations; i++) { + const rawUpdateElements = generateElements(updateSize, { random: true, size: elementOptions.size }) + const rawIndices = rawUpdateElements.map(() => Math.floor(Math.random() * elementCount)) + const indices = rawIndices.filter((index, i) => rawIndices.indexOf(index) === i).sort((a, b) => a - b) + const updateElements = rawUpdateElements.slice(0, indices.length) + + const { newMerkleTree, proof } = merkleTree.updateMulti(indices, updateElements, options) + merkleTree = newMerkleTree + root = MerkleTree.updateWithMultiProof(proof, options).root + + elements = elements.map((element, i) => { + const index = indices.indexOf(i) + + return index >= 0 ? updateElements[index] : element + }) + + expect(root.equals(merkleTree.root)).to.be.true + } + + const finalMerkleTree = new MerkleTree(elements, options) + + expect(root.equals(finalMerkleTree.root)).to.be.true +} + +export const testSingleAppend = (elementCount: number, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const appendElement = generateElements(1, { seed: '11' })[0] + const { newMerkleTree, proof } = merkleTree.appendSingle(appendElement, options) + const { root, elementCount: newElementCount } = MerkleTree.appendWithAppendProof(proof, options) + const newElements = elements.concat(appendElement) + const freshMerkleTree = new MerkleTree(newElements, options) + + expect(root.equals(newMerkleTree.root)).to.be.true + expect(root.equals(freshMerkleTree.root)).to.be.true + expect(newElementCount).to.equal(newMerkleTree.elements.length) + expect(newElementCount).to.equal(freshMerkleTree.elements.length) +} + +export const testConsecutiveSingleAppend = (iterations: number, elementCount: number, options: proofOptions = defaultProofOptions, elementOptions: elementOptions = defaultElementOptions) => { + let elements = generateElements(elementCount, { seed: 'cc', size: elementOptions.size }) + let merkleTree = new MerkleTree(elements, options) + let root = null + + for (let i = 0; i < iterations; i++) { + const appendElement = generateElements(1, { random: true, size: elementOptions.size })[0] + const { newMerkleTree, proof } = merkleTree.appendSingle(appendElement, options) + merkleTree = newMerkleTree + const results = MerkleTree.appendWithAppendProof(proof, options) + root = results.root + elements.push(appendElement) + + expect(root.equals(merkleTree.root)).to.equal(true) + expect(results.elementCount).to.equal(merkleTree.elements.length) + } + + const finalMerkleTree = new MerkleTree(elements, options) + + expect(root.equals(finalMerkleTree.root)).to.be.true +} + +export const testMultiAppend = (elementCount: number, appendSize: number, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const appendElements = generateElements(appendSize, { seed: '11' }) + const { newMerkleTree, proof } = merkleTree.appendMulti(appendElements, options) + const { root, elementCount: newElementCount } = MerkleTree.appendWithAppendProof(proof, options) + const newElements = elements.concat(appendElements) + const freshMerkleTree = new MerkleTree(newElements, options) + + expect(root.equals(newMerkleTree.root)).to.be.true + expect(root.equals(freshMerkleTree.root)).to.be.true + expect(newElementCount).to.equal(newMerkleTree.elements.length) + expect(newElementCount).to.equal(freshMerkleTree.elements.length) +} + +export const testConsecutiveMultiAppend = (iterations: number, elementCount: number, appendSize: number, options: proofOptions = defaultProofOptions, elementOptions: elementOptions = defaultElementOptions) => { + let elements = generateElements(elementCount, { seed: 'cc', size: elementOptions.size }) + let merkleTree = new MerkleTree(elements, options) + let root = null + + for (let i = 0; i < iterations; i++) { + const appendElements = generateElements(Math.ceil(Math.random() * appendSize), { random: true, size: elementOptions.size }) + const { newMerkleTree, proof } = merkleTree.appendMulti(appendElements, options) + merkleTree = newMerkleTree + const results = MerkleTree.appendWithAppendProof(proof, options) + root = results.root + elements = elements.concat(appendElements) + + expect(root.equals(merkleTree.root)).to.be.true + expect(results.elementCount).to.equal(merkleTree.elements.length) + } + + const finalMerkleTree = new MerkleTree(elements, options) + + expect(root.equals(finalMerkleTree.root)).to.be.true +} + +export const testSingleUpdateSingleAppend = (elementCount: number, index: number, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const updateElement = generateElements(1, { seed: '11' })[0] + const appendElement = generateElements(1, { seed: '22' })[0] + const { newMerkleTree, proof } = merkleTree.updateAndAppend({ index, updateElement, appendElement }, options) + const { root, elementCount: newElementCount } = MerkleTree.updateAndAppendWithCombinedProof(proof, options) + const updatedElements = elements.map((e, i) => (index == i ? updateElement : e)) + const newElements = updatedElements.concat(appendElement) + const freshMerkleTree = new MerkleTree(newElements, options) + + expect(root.equals(newMerkleTree.root)).to.be.true + expect(root.equals(freshMerkleTree.root)).to.be.true + expect(newElementCount).to.equal(newMerkleTree.elements.length) + expect(newElementCount).to.equal(freshMerkleTree.elements.length) +} + +export const testMultiUpdateMultiAppend = (elementCount: number, indices: Array, appendSize: number, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const updateElements = generateElements(indices.length, { seed: '11' }) + const appendElements = generateElements(appendSize, { seed: '22' }) + const { newMerkleTree, proof } = merkleTree.updateAndAppend({ indices, updateElements, appendElements }, options) + const { root, elementCount: newElementCount } = MerkleTree.updateAndAppendWithCombinedProof(proof, options) + + const updatedElements = elements.map((e, i) => { + const index = indices.indexOf(i) + + return index >= 0 ? updateElements[index] : e + }) + + const newElements = updatedElements.concat(appendElements) + const freshMerkleTree = new MerkleTree(newElements, options) + + expect(root.equals(newMerkleTree.root)).to.be.true + expect(root.equals(freshMerkleTree.root)).to.be.true + expect(newElementCount).to.equal(newMerkleTree.elements.length) + expect(newElementCount).to.equal(freshMerkleTree.elements.length) +} + +export const testConsecutiveSingleUpdateSingleAppend = (iterations: number, elementCount: number, options: proofOptions = defaultProofOptions) => { + let elements = generateElements(elementCount, { seed: 'cc' }) + let merkleTree = new MerkleTree(elements, options) + let root = null + + for (let i = 0; i < iterations; i++) { + const minimumIndex = merkleTree.minimumCombinedProofIndex + const index = Math.floor(Math.random() * (elements.length - minimumIndex) + minimumIndex) + const updateElement = generateElements(1, { random: true })[0] + const appendElement = generateElements(1, { random: true })[0] + + const { newMerkleTree, proof } = merkleTree.updateAndAppend({ index, updateElement, appendElement }, options) + merkleTree = newMerkleTree + const results = MerkleTree.updateAndAppendWithCombinedProof(proof, options) + root = results.root + + elements = elements.map((element, i) => (i === index ? updateElement : element)).concat(appendElement) + + expect(root.equals(merkleTree.root)).to.be.true + expect(results.elementCount).to.equal(merkleTree.elements.length) + } + + const finalMerkleTree = new MerkleTree(elements, options) + + expect(root.equals(finalMerkleTree.root)).to.be.true +} + +export const testConsecutiveMultiUpdateMultiAppend = (iterations: number, elementCount: number, updateSize: number, appendSize: number, options: proofOptions = defaultProofOptions) => { + let elements = generateElements(elementCount, { seed: 'cc' }) + let merkleTree = new MerkleTree(elements, options) + let root = null + + for (let i = 0; i < iterations; i++) { + const rawUpdateElements = generateElements(updateSize, { random: true }) + const rawUpdateIndices = rawUpdateElements.map(() => Math.floor(Math.random() * elements.length)) + const minimumIndex = merkleTree.minimumCombinedProofIndex + rawUpdateIndices[0] = Math.floor(Math.random() * (elements.length - minimumIndex) + minimumIndex) + const indices = rawUpdateIndices.filter((index, i, arr) => arr.indexOf(index) === i).sort((a, b) => a - b) + const updateElements = rawUpdateElements.slice(0, indices.length) + const appendElements = generateElements(Math.ceil(Math.random() * appendSize), { random: true }) + + const { newMerkleTree, proof } = merkleTree.updateAndAppend({ indices, updateElements, appendElements }, options) + merkleTree = newMerkleTree + const results = MerkleTree.updateAndAppendWithCombinedProof(proof, options) + root = results.root + + elements = elements + .map((element, i) => { + const index = indices.indexOf(i) + + return index >= 0 ? updateElements[index] : element + }) + .concat(appendElements) + + expect(root.equals(merkleTree.root)).to.be.true + expect(results.elementCount).to.equal(merkleTree.elements.length) + } + + const finalMerkleTree = new MerkleTree(elements, options) + + expect(root.equals(finalMerkleTree.root)).to.be.true +} + +export const testSingleUseSingleAppend = (elementCount: number, index: number, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const appendElement = generateElements(1, { seed: '22' })[0] + const { newMerkleTree, proof } = merkleTree.useAndAppend(index, appendElement, options) + const { root, elementCount: newElementCount } = MerkleTree.appendWithCombinedProof(proof, options) + const newElements = elements.concat(appendElement) + const freshMerkleTree = new MerkleTree(newElements, options) + + expect(root.equals(newMerkleTree.root)).to.be.true + expect(root.equals(freshMerkleTree.root)).to.be.true + expect(newElementCount).to.equal(newMerkleTree.elements.length) + expect(newElementCount).to.equal(freshMerkleTree.elements.length) +} + +export const testConsecutiveSingleUseSingleAppend = (iterations: number, elementCount: number, options: proofOptions = defaultProofOptions) => { + let elements = generateElements(elementCount, { seed: 'cc' }) + let merkleTree = new MerkleTree(elements, options) + let root = null + + for (let i = 0; i < iterations; i++) { + const minimumIndex = merkleTree.minimumCombinedProofIndex + const index = Math.floor(Math.random() * (elements.length - minimumIndex) + minimumIndex) + const appendElement = generateElements(1, { random: true })[0] + + const { newMerkleTree, proof } = merkleTree.useAndAppend(index, appendElement, options) + merkleTree = newMerkleTree + const results = MerkleTree.appendWithCombinedProof(proof, options) + root = results.root + + elements = elements.concat(appendElement) + + expect(root.equals(merkleTree.root)).to.be.true + expect(results.elementCount).to.equal(merkleTree.elements.length) + } + + const finalMerkleTree = new MerkleTree(elements, options) + + expect(root.equals(finalMerkleTree.root)).to.be.true +} + +export const testMultiUseMultiAppend = (elementCount: number, indices: Array, appendSize: number, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const aElements = generateElements(appendSize, { seed: '22' }) + const { newMerkleTree, proof } = merkleTree.useAndAppend(indices, aElements, options) + const { root, elementCount: newElementCount } = MerkleTree.appendWithCombinedProof(proof, options) + const newElements = elements.concat(aElements) + const freshMerkleTree = new MerkleTree(newElements, options) + + expect(root.equals(newMerkleTree.root)).to.be.true + expect(root.equals(freshMerkleTree.root)).to.be.true + expect(newElementCount).to.equal(newMerkleTree.elements.length) + expect(newElementCount).to.equal(freshMerkleTree.elements.length) +} + +export const testConsecutiveMultiUseMultiAppend = (iterations: number, elementCount: number, useSize: number, appendSize: number, options: proofOptions = defaultProofOptions) => { + let elements = generateElements(elementCount, { seed: 'cc' }) + let merkleTree = new MerkleTree(elements, options) + let root = null + + for (let i = 0; i < iterations; i++) { + const rawIndices = Array(useSize) + .fill(null) + .map(() => Math.floor(Math.random() * elements.length)) + const minimumIndex = merkleTree.minimumCombinedProofIndex + rawIndices[0] = Math.floor(Math.random() * (elements.length - minimumIndex) + minimumIndex) + const indices = rawIndices.filter((index, i, arr) => arr.indexOf(index) === i).sort((a, b) => a - b) + const appendElements = generateElements(Math.ceil(Math.random() * appendSize), { random: true }) + + const { newMerkleTree, proof } = merkleTree.useAndAppend(indices, appendElements, options) + merkleTree = newMerkleTree + const results = MerkleTree.appendWithCombinedProof(proof, options) + root = results.root + elements = elements.concat(appendElements) + + expect(root.equals(merkleTree.root)).to.be.true + expect(results.elementCount).to.equal(merkleTree.elements.length) + } + + const finalMerkleTree = new MerkleTree(elements, options) + + expect(root.equals(finalMerkleTree.root)).to.be.true +} diff --git a/js/src/ts/tests/helpers/partial-tree-helpers.ts b/js/src/ts/tests/helpers/partial-tree-helpers.ts new file mode 100644 index 0000000..2beb7d2 --- /dev/null +++ b/js/src/ts/tests/helpers/partial-tree-helpers.ts @@ -0,0 +1,218 @@ + +import { expect } from 'chai' +import { generateElements } from './index' +import { MerkleTree, PartialMerkleTree } from '../../index' +import { proof } from '../../common' + +export interface expectedPartialTree extends proof { + tree?: Array +} + +export const testTreeFromSingleProof = (elementCount: number, seed: string, index: number, expected: expectedPartialTree, options) => { + const elements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateSingleProof(index, options) + const partialTree = PartialMerkleTree.fromSingleProof(proof, options) + + expect(partialTree._sortedHash).to.equal(merkleTree._sortedHash) + expect(partialTree._unbalanced).to.equal(merkleTree._unbalanced) + expect(partialTree._elementPrefix.equals(merkleTree._elementPrefix)).to.be.true + expect(partialTree._depth).to.equal(merkleTree._depth) + expect(partialTree._elements.map((e) => e && e.toString('hex'))).to.deep.equal(expected.elements) + expect(partialTree._tree.map((n) => n && n.toString('hex'))).to.deep.equal(expected.tree) +} + +export const testTreeFromSingleUpdateProof = (elementCount: number, seed: string, index: number, expected: expectedPartialTree, options) => { + const elements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(elements, options) + const updateElement = generateElements(1, { seed: '11' })[0] + const { proof, newMerkleTree } = merkleTree.updateSingle(index, updateElement, options) + const partialTree = PartialMerkleTree.fromSingleUpdateProof(proof, options) + + expect(partialTree._sortedHash).to.equal(newMerkleTree._sortedHash) + expect(partialTree._unbalanced).to.equal(newMerkleTree._unbalanced) + expect(partialTree._elementPrefix.equals(newMerkleTree._elementPrefix)).to.be.true + expect(partialTree._depth).to.equal(newMerkleTree._depth) + expect(partialTree._elements.map((e) => e && e.toString('hex'))).to.deep.equal(expected.elements) + expect(partialTree._tree.map((n) => n && n.toString('hex'))).to.deep.equal(expected.tree) +} + +export const testTreeFromMultiProof = (elementCount: number, seed: string, indices, expected: expectedPartialTree, options) => { + const elements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateMultiProof(indices, options) + const partialTree = PartialMerkleTree.fromMultiProof(proof, options) + + expect(partialTree._sortedHash).to.equal(merkleTree._sortedHash) + expect(partialTree._unbalanced).to.equal(merkleTree._unbalanced) + expect(partialTree._elementPrefix.equals(merkleTree._elementPrefix)).to.be.true + expect(partialTree._depth).to.equal(merkleTree._depth) + expect(partialTree._elements.map((e) => e && e.toString('hex'))).to.deep.equal(expected.elements) + expect(partialTree._tree.map((n) => n && n.toString('hex'))).to.deep.equal(expected.tree) +} + +export const testTreeFromMultiUpdateProof = (elementCount: number, seed: string, indices, expected: expectedPartialTree, options) => { + const elements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(elements, options) + const updateElements = generateElements(indices.length, { seed: '11' }) + const { proof, newMerkleTree } = merkleTree.updateMulti(indices, updateElements, options) + const partialTree = PartialMerkleTree.fromMultiUpdateProof(proof, options) + + expect(partialTree._sortedHash).to.equal(newMerkleTree._sortedHash) + expect(partialTree._unbalanced).to.equal(newMerkleTree._unbalanced) + expect(partialTree._elementPrefix.equals(newMerkleTree._elementPrefix)).to.be.true + expect(partialTree._depth).to.equal(newMerkleTree._depth) + expect(partialTree._elements.map((e) => e && e.toString('hex'))).to.deep.equal(expected.elements) + expect(partialTree._tree.map((n) => n && n.toString('hex'))).to.deep.equal(expected.tree) +} + +export const testTreeFromSingleAppendProof = (elementCount: number, seed: string, expected: expectedPartialTree, options) => { + const elements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(elements, options) + const appendElement = generateElements(1, { seed: '22' })[0] + const { proof, newMerkleTree } = merkleTree.appendSingle(appendElement, options) + const partialTree = PartialMerkleTree.fromAppendProof(proof, options) + + expect(partialTree._sortedHash).to.equal(newMerkleTree._sortedHash) + expect(partialTree._unbalanced).to.equal(newMerkleTree._unbalanced) + expect(partialTree._elementPrefix.equals(newMerkleTree._elementPrefix)).to.be.true + expect(partialTree._depth).to.equal(newMerkleTree._depth) + expect(partialTree._elements.map((e) => e && e.toString('hex'))).to.deep.equal(expected.elements) + expect(partialTree._tree.map((n) => n && n.toString('hex'))).to.deep.equal(expected.tree) +} + +export const testTreeFromMultiAppendProof = (elementCount: number, seed: string, appendCount, expected: expectedPartialTree, options) => { + const elements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(elements, options) + const appendElements = generateElements(appendCount, { seed: '22' }) + const { proof, newMerkleTree } = merkleTree.appendMulti(appendElements, options) + const partialTree = PartialMerkleTree.fromAppendProof(proof, options) + + expect(partialTree._sortedHash).to.equal(newMerkleTree._sortedHash) + expect(partialTree._unbalanced).to.equal(newMerkleTree._unbalanced) + expect(partialTree._elementPrefix.equals(newMerkleTree._elementPrefix)).to.be.true + expect(partialTree._depth).to.equal(newMerkleTree._depth) + expect(partialTree._elements.map((e) => e && e.toString('hex'))).to.deep.equal(expected.elements) + expect(partialTree._tree.map((n) => n && n.toString('hex'))).to.deep.equal(expected.tree) +} + +export const testGenerateSingleProofFromPartial = (elementCount: number, index: number, options) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateSingleProof(index, options) + const partialTree = PartialMerkleTree.fromSingleProof(proof, options) + + const proofFromPartial = partialTree.generateSingleProof(index, options) + const proofIsValid = MerkleTree.verifySingleProof(proofFromPartial, options) + + expect(proofIsValid).to.be.true + expect(proofFromPartial.root.equals(proof.root)).to.be.true +} + +export const testGenerateSingleUpdateProofFromSinglePartial = (elementCount: number, index: number, options) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const updateElement = generateElements(1, { seed: '11' })[0] + + const merkleTree = new MerkleTree(elements, options) + const { proof: proofFromTree, newMerkleTree } = merkleTree.updateSingle(index, updateElement, options) + + const partialTree = PartialMerkleTree.fromSingleProof(proofFromTree, options) + const { proof: proofFromPartialTree, newPartialTree } = partialTree.updatePartialSingle(index, updateElement, options) + + const proofFromPartialTreeIsValid = MerkleTree.verifySingleProof(proofFromPartialTree, options) + + expect(proofFromPartialTreeIsValid).to.be.true + expect(newPartialTree.root.equals(newMerkleTree.root)).to.be.true +} + +export const testGenerateMultiUpdateProofFromMultiPartial = (elementCount: number, indices, options) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const updateElements = generateElements(indices.length, { seed: '11' }) + + const merkleTree = new MerkleTree(elements, options) + const { proof: proofFromTree, newMerkleTree } = merkleTree.updateMulti(indices, updateElements, options) + + const partialTree = PartialMerkleTree.fromMultiProof(proofFromTree, options) + const { proof: proofFromPartialTree, newPartialTree } = partialTree.updatePartialMulti(indices, updateElements, options) + + const proofFromPartialTreeIsValid = MerkleTree.verifyMultiProof(proofFromPartialTree, options) + + expect(proofFromPartialTreeIsValid).to.be.true + expect(newPartialTree.root.equals(newMerkleTree.root)).to.be.true +} + +export const testCheckElements = (elementCount: number, index: number, checkIndices, expectations, options) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateSingleProof(index, options) + const partialTree = PartialMerkleTree.fromSingleProof(proof, options) + const elementChecks = partialTree.check( + checkIndices, + checkIndices.map((i) => elements[i]) + ) + + expect(elementChecks).to.deep.equal(expectations) + checkIndices.forEach((index, i) => expect(partialTree.check(index, elements[index])).to.equal(expectations[i])) +} + +export const testSetElement = (elementCount: number, index: number, setIndex, options) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateSingleProof(index, options) + const partialTree = PartialMerkleTree.fromSingleProof(proof, options) + const newPartialTree = partialTree.set(setIndex, elements[setIndex]) + + expect(newPartialTree._elements[index].equals(elements[index])).to.be.true + expect(newPartialTree._elements[setIndex].equals(elements[setIndex])).to.be.true + const expectedTreeNodes = partialTree._tree.map((n) => n && n.toString('hex')) + const newLeafIndex = (newPartialTree._tree.length >> 1) + setIndex + expectedTreeNodes[newLeafIndex] = merkleTree._tree[newLeafIndex].toString('hex') + expect(newPartialTree._tree.map((n) => n && n.toString('hex'))).to.deep.equal(expectedTreeNodes) +} + +export const testSetElements = (elementCount: number, index: number, setIndices, options) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateSingleProof(index, options) + const partialTree = PartialMerkleTree.fromSingleProof(proof, options) + const newPartialTree = partialTree.set( + setIndices, + setIndices.map((i) => elements[i]) + ) + + setIndices.concat(index).forEach((i) => expect(newPartialTree._elements[i].equals(elements[i])).to.be.true) + const expectedTreeNodes = partialTree._tree.map((n) => n && n.toString('hex')) + + setIndices.forEach((setIndex) => { + const newLeafIndex = (newPartialTree._tree.length >> 1) + setIndex + expectedTreeNodes[newLeafIndex] = merkleTree._tree[newLeafIndex].toString('hex') + }) + + expect(newPartialTree._tree.map((n) => n && n.toString('hex'))).to.deep.equal(expectedTreeNodes) +} + +export const testAppendElement = (elementCount: number, options) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const index = elementCount - 1 + const proof = merkleTree.generateSingleProof(index, options) + const partialTree = PartialMerkleTree.fromSingleProof(proof, options) + const appendElement = generateElements(1, { seed: '22' })[0] + const newPartialTree = partialTree.append(appendElement) + const newMerkleTree = new MerkleTree(elements.concat(appendElement), options) + + expect(newPartialTree.root.equals(newMerkleTree.root)).to.be.true +} + +export const testAppendElements = (elementCount: number, appendElementCount, options) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const index = elementCount - 1 + const proof = merkleTree.generateSingleProof(index, options) + const partialTree = PartialMerkleTree.fromSingleProof(proof, options) + const appendElements = generateElements(appendElementCount, { seed: '22' }) + const newPartialTree = partialTree.append(appendElements) + const newMerkleTree = new MerkleTree(elements.concat(appendElements), options) + + expect(newPartialTree.root.equals(newMerkleTree.root)).to.be.true +}; \ No newline at end of file diff --git a/js/src/ts/tests/helpers/proof-helpers.ts b/js/src/ts/tests/helpers/proof-helpers.ts new file mode 100644 index 0000000..b5cf42a --- /dev/null +++ b/js/src/ts/tests/helpers/proof-helpers.ts @@ -0,0 +1,318 @@ +import { expect } from 'chai' +import { generateElements } from './index' +import { MerkleTree } from '../../index' +import { defaultProofOptions, proof, proofOptions, updateAndAppendProof } from '../../common' + + +export interface expectedProof extends updateAndAppendProof { + minimumIndex?: number +} + +export const testSingleProofGeneration = (elementCount: number, seed: string, index: number, expected: expectedProof, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateSingleProof(index, options) + + expect(proof.root.equals(merkleTree.root)).to.be.true + expect(proof.index).to.equal(index) + expect(proof.element.equals(elements[index])).to.be.true + + if (options.compact) { + expect(proof.compactProof.length).to.equal(expected.compactProof.length) + proof.compactProof.forEach((d, i) => expect(d.toString('hex')).to.equal(expected.compactProof[i].toString())) + return + } + + expect(proof.elementCount).to.equal(elementCount) + expect(proof.decommitments.length).to.equal(expected.decommitments.length) + proof.decommitments.forEach((d, i) => expect(d.toString('hex')).to.equal(expected.decommitments[i].toString())) +} + +export const compareSingleProofs = (elementCount: number, index: number, optionsA: proofOptions = defaultProofOptions, optionsB: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const treeA = new MerkleTree(elements, optionsA) + const proofA = treeA.generateSingleProof(index) + const treeB = new MerkleTree(elements, optionsB) + const proofB = treeB.generateSingleProof(index) + + expect(proofA.root.equals(proofB.root)).to.be.true + expect(proofA.elementCount).to.equal(proofB.elementCount) + expect(proofA.index).to.equal(proofB.index) + expect(proofA.element.equals(proofB.element)).to.be.true + expect(proofA.decommitments.length).to.equal(proofB.decommitments.length) + proofA.decommitments.forEach((d, i) => expect(d.equals(proofB.decommitments[i])).to.be.true) +} + +export const testSingleProofVerification = (elementCount: number, index: number, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateSingleProof(index, options) + const proofValid = MerkleTree.verifySingleProof(proof, options) + + expect(proofValid).to.be.true +} + +export const testMultiProofGeneration = (elementCount: number, seed: string, indices: Array, expected: expectedProof, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateMultiProof(indices, options) + + expect(proof.root.equals(merkleTree.root)).to.be.true + expect(proof.elements.length).to.equal(indices.length) + proof.elements.forEach((e, i) => expect(e.equals(elements[indices[i]])).to.be.true) + + if (options.compact) { + expect(proof.compactProof.length).to.equal(expected.compactProof.length) + proof.compactProof.forEach((p, i) => expect(p.toString('hex')).to.equal(expected.compactProof[i].toString())) + return + } + + if (options.indexed) { + expect(proof.indices).to.deep.equal(indices) + } else { + expect(proof.indices).to.equal(undefined) + expect(proof.flags).to.deep.equal(expected.flags) + expect(proof.skips).to.deep.equal(expected.skips) + } + + if (!options.sortedHash) { + expect(proof.orders).to.deep.equal(expected.orders) + } + + expect(proof.elementCount).to.equal(elementCount) + expect(proof.decommitments.length).to.equal(expected.decommitments.length) + proof.decommitments.forEach((d, i) => expect(d.toString('hex')).to.equal(expected.decommitments[i].toString())) +} + +export const compareMultiProofs = (elementCount: number, indices: Array, optionsA: proofOptions = defaultProofOptions, optionsB: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const treeA = new MerkleTree(elements, optionsA) + const proofA = treeA.generateMultiProof(indices, optionsA) + const treeB = new MerkleTree(elements, optionsB) + const proofB = treeB.generateMultiProof(indices, optionsB) + + expect(proofA.root.equals(proofB.root)).to.be.true + expect(proofA.elementCount).to.equal(proofB.elementCount) + proofA.elements.forEach((e, i) => expect(e.equals(proofB.elements[i])).to.be.true) + expect(proofA.elements.length).to.equal(proofB.elements.length) + + if (optionsA.indexed && optionsB.indexed) { + expect(proofA.indices).to.deep.equal(proofB.indices) + return + } + + if (optionsA.compact && optionsB.compact) { + proofA.compactProof.forEach((p, i) => expect(p.equals(proofB.compactProof[i])).to.be.true) + expect(proofA.compactProof.length).to.equal(proofB.compactProof.length) + return + } + + proofA.decommitments.forEach((d, i) => expect(d.equals(proofB.decommitments[i])).to.be.true) + expect(proofA.decommitments.length).to.equal(proofB.decommitments.length) + expect(proofA.flags).to.deep.equal(proofB.flags) + expect(proofA.skips).to.deep.equal(proofB.skips) +} + +export const testMultiProofVerification = (elementCount: number, indices: Array, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateMultiProof(indices, options) + const proofValid = MerkleTree.verifyMultiProof(proof, options) + + expect(proofValid).to.be.true +} + +export const testMultiProofIndicesInferring = (elementCount: number, indices: Array, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateMultiProof(indices, options) + const inferredIndices = MerkleTree.getMultiProofIndices(proof) + + expect(inferredIndices).to.deep.equal(indices) +} +export const testAppendProofGeneration = (elementCount: number, seed: string, expected: expectedProof, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateAppendProof(options) + + expect(proof.root.equals(merkleTree.root)).to.be.true + + if (options.compact) { + proof.compactProof.forEach((d, i) => expect(d.toString('hex')).to.equal(expected.compactProof[i])) + expect(proof.compactProof.length).to.equal(expected.compactProof.length) + return + } + + expect(proof.elementCount).to.equal(elementCount) + proof.decommitments.forEach((d, i) => expect(d.toString('hex')).to.equal(expected.decommitments[i])) + expect(proof.decommitments.length).to.equal(expected.decommitments.length) +} + +export const testAppendProofVerification = (elementCount: number, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateAppendProof(options) + const proofValid = MerkleTree.verifyAppendProof(proof, options) + + expect(proofValid).to.be.true +} + +export const testCombinedProofMinimumIndex = (elementCount: number, expected: expectedProof, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount) + const merkleTree = new MerkleTree(elements, options) + const minimumIndex = merkleTree.minimumCombinedProofIndex + + expect(minimumIndex).to.equal(expected.minimumIndex) +} + +export const testSingleUpdateSingleAppendProofGeneration = (elementCount: number, seed: string, index: number, options: proofOptions = defaultProofOptions) => { + const originalElements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(originalElements, options) + const updateElement = generateElements(1, { seed: '11' })[0] + const appendElement = generateElements(1, { seed: '22' })[0] + const combinedProof = merkleTree.generateUpdateAppendProof({ index, updateElement, appendElement }, options) + const { element, updateElement: rUpdateElement, appendElement: rAppendElement } = combinedProof + const singleProof = merkleTree.generateSingleProof(index, options) + + expect(element.equals(originalElements[index])).to.be.true + expect(updateElement.equals(rUpdateElement)).to.be.true + expect(appendElement.equals(rAppendElement)).to.be.true + + if (options.compact) { + combinedProof.compactProof.forEach((p, i) => expect(p.equals(singleProof.compactProof[i])).to.be.true) + expect(combinedProof.compactProof.length).to.equal(singleProof.compactProof.length) + return + } + + expect(combinedProof.elementCount).to.equal(singleProof.elementCount) + combinedProof.decommitments.forEach((d, i) => expect(d.equals(singleProof.decommitments[i])).to.be.true) + expect(combinedProof.decommitments.length).to.equal(singleProof.decommitments.length) +} + +export const testMultiUpdateMultiAppendProofGeneration = (elementCount: number, seed: string, indices: Array, appendSize: number, options: proofOptions = defaultProofOptions) => { + const originalElements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(originalElements, options) + const updateElements = generateElements(indices.length, { seed: '11' }) + const appendElements = generateElements(appendSize, { seed: '22' }) + const combinedProof = merkleTree.generateUpdateAppendProof({ indices, updateElements, appendElements }, options) + const { elements, updateElements: rUpdateElements, appendElements: rAppendElements } = combinedProof + const multiProof = merkleTree.generateMultiProof(indices, options) + + elements.forEach((e, i) => expect(e.equals(originalElements[indices[i]])).to.be.true) + expect(elements.length).to.equal(indices.length) + rUpdateElements.forEach((e, i) => expect(e.equals(updateElements[i])).to.be.true) + expect(rUpdateElements.length).to.equal(updateElements.length) + rAppendElements.forEach((e, i) => expect(e.equals(appendElements[i])).to.be.true) + expect(rAppendElements.length).to.equal(appendElements.length) + + if (options.compact) { + combinedProof.compactProof.forEach((p, i) => expect(p.equals(multiProof.compactProof[i])).to.be.true) + expect(combinedProof.compactProof.length).to.equal(multiProof.compactProof.length) + return + } + + if (!options.sortedHash) { + expect(combinedProof.orders).to.deep.equal(multiProof.orders) + } + + expect(combinedProof.elementCount).to.equal(multiProof.elementCount) + combinedProof.decommitments.forEach((d, i) => expect(d.equals(multiProof.decommitments[i])).to.be.true) + expect(combinedProof.decommitments.length).to.equal(multiProof.decommitments.length) + expect(combinedProof.flags).to.deep.equal(multiProof.flags) + expect(combinedProof.skips).to.deep.equal(multiProof.skips) +} + +export const testCombinedProofVerification = (elementCount: number, indices: Array, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateCombinedProof(indices, options) + const proofValid = MerkleTree.verifyCombinedProof(proof, options) + + expect(proofValid).to.be.true +} + +export const testSingleUseSingleAppendProofGeneration = (elementCount: number, seed: string, index: number, options: proofOptions = defaultProofOptions) => { + const originalElements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(originalElements, options) + const appendElement = generateElements(1, { seed: '22' })[0] + const combinedProof = merkleTree.generateUseAppendProof({ index, appendElement }, options) + const { element, appendElement: rAppendElement } = combinedProof + const singleProof = merkleTree.generateSingleProof(index, options) + + expect(element.equals(originalElements[index])).to.be.true + expect(appendElement.equals(rAppendElement)).to.be.true + + if (options.compact) { + combinedProof.compactProof.forEach((p, i) => expect(p.equals(singleProof.compactProof[i])).to.be.true) + expect(combinedProof.compactProof.length).to.equal(singleProof.compactProof.length) + return + } + + expect(combinedProof.elementCount).to.equal(singleProof.elementCount) + combinedProof.decommitments.forEach((d, i) => expect(d.equals(singleProof.decommitments[i])).to.be.true) + expect(combinedProof.decommitments.length).to.equal(singleProof.decommitments.length) +} + +export const testMultiUseMultiAppendProofGeneration = (elementCount: number, seed: string, indices: Array, appendSize: number, options: proofOptions = defaultProofOptions) => { + const originalElements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(originalElements, options) + const appendElements = generateElements(appendSize, { seed: '22' }) + const combinedProof = merkleTree.generateUseAppendProof({ indices, appendElements }, options) + const { elements, appendElements: rAppendElements } = combinedProof + const multiProof = merkleTree.generateMultiProof(indices, options) + + elements.forEach((e, i) => expect(e.equals(originalElements[indices[i]])).to.be.true) + expect(elements.length).to.equal(indices.length) + rAppendElements.forEach((e, i) => expect(e.equals(appendElements[i])).to.be.true) + expect(rAppendElements.length).to.equal(appendElements.length) + + if (options.compact) { + combinedProof.compactProof.forEach((p, i) => expect(p.equals(multiProof.compactProof[i])).to.be.true) + expect(combinedProof.compactProof.length).to.equal(multiProof.compactProof.length) + return + } + + if (!options.sortedHash) { + expect(combinedProof.orders).to.deep.equal(multiProof.orders) + } + + expect(combinedProof.elementCount).to.equal(multiProof.elementCount) + combinedProof.decommitments.forEach((d, i) => expect(d.equals(multiProof.decommitments[i])).to.be.true) + expect(combinedProof.decommitments.length).to.equal(multiProof.decommitments.length) + expect(combinedProof.flags).to.deep.equal(multiProof.flags) + expect(combinedProof.skips).to.deep.equal(multiProof.skips) +} + +export const testSizeProofGeneration = (elementCount: number, seed: string, expected: expectedProof, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateSizeProof(options) + + expect(proof.root.equals(merkleTree.root)).to.be.true + expect(proof.elementCount).to.equal(elementCount) + + if (options.simple) { + expect(proof.element.toString('hex')).to.equal(expected.element) + return + } + + if (options.compact) { + proof.compactProof.forEach((d, i) => expect(d.toString('hex')).to.equal(expected.compactProof[i])) + expect(proof.compactProof.length).to.equal(expected.compactProof.length) + return + } + + proof.decommitments.forEach((d, i) => expect(d.toString('hex')).to.equal(expected.decommitments[i])) + expect(proof.decommitments.length).to.equal(expected.decommitments.length) +} + +export const testSizeProofVerification = (elementCount: number, options: proofOptions = defaultProofOptions) => { + const elements = generateElements(elementCount, { seed: 'ff' }) + const merkleTree = new MerkleTree(elements, options) + const proof = merkleTree.generateSizeProof(options) + const proofValid = MerkleTree.verifySizeProof(proof, options) + + expect(proofValid).to.be.true +} + + diff --git a/js/src/ts/tests/merkle.test.ts b/js/src/ts/tests/merkle.test.ts new file mode 100644 index 0000000..d23d44a --- /dev/null +++ b/js/src/ts/tests/merkle.test.ts @@ -0,0 +1,4402 @@ +import { testBuildTree, compareTrees, testSingleUpdate, testConsecutiveSingleUpdate, testMultiUpdate, testConsecutiveMultiUpdate, testSingleAppend, testConsecutiveSingleAppend, testMultiAppend, testConsecutiveMultiAppend, testSingleUpdateSingleAppend, testMultiUpdateMultiAppend, testConsecutiveSingleUpdateSingleAppend, testConsecutiveMultiUpdateMultiAppend, testSingleUseSingleAppend, testConsecutiveSingleUseSingleAppend, testMultiUseMultiAppend, testConsecutiveMultiUseMultiAppend } from './helpers/merkle-tree-helpers' +import { testSingleProofGeneration, compareSingleProofs, testSingleProofVerification, testMultiProofGeneration, compareMultiProofs, testMultiProofVerification, testMultiProofIndicesInferring, testAppendProofGeneration, testAppendProofVerification, testCombinedProofMinimumIndex, testSingleUpdateSingleAppendProofGeneration, testMultiUpdateMultiAppendProofGeneration, testCombinedProofVerification, testSingleUseSingleAppendProofGeneration, testMultiUseMultiAppendProofGeneration, testSizeProofGeneration, testSizeProofVerification } from './helpers/proof-helpers' + + +describe('Merkle Trees', () => { + describe('Merkle Tree Construction', () => { + describe('Balanced', () => { + it('should build a 8-element Merkle Tree.', () => { + const expected = { + root: 'd2fa9d47845f1571f1318afaaabc63a55cc57af3f511e42fc30e05f171d6853d', + elementRoot: '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4', + depth: 3, + } + + testBuildTree(8, 'ff', expected, { unbalanced: false, sortedHash: false }) + }) + + it('should build a 1-element Merkle Tree.', () => { + const expected = { + root: 'c83b51dc238800a2852366108ab7df1e32b35e99905c5d845ff5a652f0fb58a8', + elementRoot: '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60', + depth: 0, + } + + testBuildTree(1, 'ff', expected, { unbalanced: false, sortedHash: false }) + }) + + it('should build a balanced sorted-hash 8-element Merkle Tree.', () => { + const expected = { + root: '6764fd6d226590b844285c3d0f1e12bbd19cb7d1ee8277b0fb5b9b45efbbffb6', + elementRoot: '7f8dc34b7b4e06eff546283358ff8d7a988b62bc266f6337f8234c9a84778221', + depth: 3, + } + + testBuildTree(8, 'ff', expected, { unbalanced: false, sortedHash: true }) + }) + + it('should build a balanced sorted-hash 1-element Merkle Tree.', () => { + const expected = { + root: 'c83b51dc238800a2852366108ab7df1e32b35e99905c5d845ff5a652f0fb58a8', + elementRoot: '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60', + depth: 0, + } + + testBuildTree(1, 'ff', expected, { unbalanced: false, sortedHash: true }) + }) + }) + + describe('Unbalanced', () => { + it('should build an 9-element Merkle Tree.', () => { + const expected = { + root: '743605bc7fcb07d66ecf3f2b5fcea24bfb27901bfbdb7baf6a194aa45d62461d', + elementRoot: '5449a839359e08115bbc14ed1795892a3a8562d583744e1a1fa146d273ff1f55', + depth: 4, + } + + testBuildTree(9, 'ff', expected, { unbalanced: true, sortedHash: false }) + }) + + it('should build a sorted-hash 9-element Merkle Tree.', () => { + const expected = { + root: '4c10104ea544f26190809c1117a092b18c8d7ab892f23c30a0f0cdb2c5242c48', + elementRoot: '86620d93d22f2d06344f81166356ed881cfdc36c8b35a7115e8b0daad4d56ee4', + depth: 4, + } + + testBuildTree(9, 'ff', expected, { unbalanced: true, sortedHash: true }) + }) + + it('should build a 28-element Merkle Tree.', () => { + const expected = { + root: 'c50d9f940bf3e7267d2c4645ef2b99d774a91582253af1d086377fb219b59e45', + elementRoot: '99d034409decb2fd31237dac23d2e037faf7d4dd896940ebb0ea580c9ffeb0af', + depth: 5, + } + + testBuildTree(28, 'ff', expected, { unbalanced: true, sortedHash: false }) + }) + + it('should build a sorted-hash 28-element Merkle Tree.', () => { + const expected = { + root: 'a5920c89398aa2837b8ad511c217dba2379e4e8b1a360de7ec00b9017fcc5f78', + elementRoot: 'cad3ecd38ce3a9ea9dd091b06889fac4b2bde73270406064582d54ed84c31087', + depth: 5, + } + + testBuildTree(28, 'ff', expected, { unbalanced: true, sortedHash: true }) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should build the same 8-element Merkle Tree.', () => { + compareTrees(8, { unbalanced: false, sortedHash: false }, { unbalanced: true, sortedHash: false }) + }) + + it('should build the same sorted-hash 8-element Merkle Tree.', () => { + compareTrees(8, { unbalanced: false, sortedHash: true }, { unbalanced: true, sortedHash: true }) + }) + }) + + describe('Empty', () => { + it('should build a 0-element Merkle Tree.', () => { + const expected = { + root: '0000000000000000000000000000000000000000000000000000000000000000', + elementRoot: '0000000000000000000000000000000000000000000000000000000000000000', + depth: 0, + } + + testBuildTree(0, 'ff', expected, { unbalanced: true, sortedHash: false }) + }) + }) + }) + + describe('Single Proofs', () => { + describe('Single Proof Generation', () => { + describe('Balanced', () => { + it('should generate a Single Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + + const expected = { + decommitments: [ + 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e', + ].map(Buffer.from), + } + + testSingleProofGeneration(8, 'ff', 2, expected, options) + }) + + it('should generate a Single Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + + const expected = { + decommitments: [], + } + + testSingleProofGeneration(1, 'ff', 0, expected, options) + }) + + it('should generate a Single Proof for a sorted-hash 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + + const expected = { + decommitments: [ + 'bc481a454a66b25fd1adf8b6b88cbcac3783d39d5ab1e4c45d114846da10274c', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e', + ].map(Buffer.from), + } + + testSingleProofGeneration(8, 'ff', 2, expected, options) + }) + + it('should generate a Single Proof for a sorted-hash 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + + const expected = { + decommitments: [], + } + + testSingleProofGeneration(1, 'ff', 0, expected, options) + }) + + it('should generate a compact Single Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000008', + 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e', + ].map(Buffer.from), + } + + testSingleProofGeneration(8, 'ff', 2, expected, options) + }) + + it('should generate a compact Single Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + + const expected = { + compactProof: ['0000000000000000000000000000000000000000000000000000000000000001'].map(Buffer.from), + } + + testSingleProofGeneration(1, 'ff', 0, expected, options) + }) + }) + + describe('Unbalanced', () => { + it('should generate a Single Proof for a 9-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const expected = { + decommitments: ['0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4'].map(Buffer.from), + } + + testSingleProofGeneration(9, 'ff', 8, expected, options) + }) + + it('should generate a Single Proof for a 27-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const expected = { + decommitments: [ + 'c7ec3e428ae2869b12c1b8e12a84e56f0d7f3cbe752cd1c3775158cf846412be', + '88d2a11c3b0935fc6a30e3b0a69fa58a84de08ea333248f23e5d747613fc04f9', + '289b3d65643b54739112fa7df258736b511ed1b5611e4f9ce681ae39fbd5fd8b', + 'c43e6f0c51c26c040dc4a40e372839ccc4a53c1762504da451819ae9e93d239a', + ].map(Buffer.from), + } + + testSingleProofGeneration(27, 'ff', 25, expected, options) + }) + + it('should generate a Single Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const expected = { + decommitments: [ + 'eb98df4415ff9a93976bb26b84f3819662fe31939e022cfa52d9061de351f6d5', + '06f8f83483a72750b8ba34cbe8fd54cc1243479b12f7b659075311dc54800203', + 'bbc26fa1ff8c9f841d4f4758cccac1def0f9929c30c949451d4e71e4ded0a681', + '4ac05d0ec2e247aad1065f712d3d6934938e4709f224a0466f558bdf2e4cbf9c', + ].map(Buffer.from), + } + + testSingleProofGeneration(100, 'ff', 97, expected, options) + }) + + it('should generate a Single Proof for a sorted-hash 9-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + + const expected = { + decommitments: ['7f8dc34b7b4e06eff546283358ff8d7a988b62bc266f6337f8234c9a84778221'].map(Buffer.from), + } + + testSingleProofGeneration(9, 'ff', 8, expected, options) + }) + + it('should generate a Single Proof for a sorted-hash 27-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + + const expected = { + decommitments: [ + '2c2cdc952c9d537709959cd357f6268fff33e5e21147f1a23db6cae78fb91eb9', + 'c62e1d7cf122111fa068da94e48ecd21cb02bba4bd41d56e9f4b69a4509a2962', + '289b3d65643b54739112fa7df258736b511ed1b5611e4f9ce681ae39fbd5fd8b', + 'c43e6f0c51c26c040dc4a40e372839ccc4a53c1762504da451819ae9e93d239a', + ].map(Buffer.from), + } + + testSingleProofGeneration(27, 'ff', 25, expected, options) + }) + + it('should generate a Single Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + + const expected = { + decommitments: [ + 'bb9a6e5787ae741c6a0e75a360aefe75ee06284ece1edddc1573ac9462945e7f', + '904afce76e0f7ccead463e22aec76018c1450afd3deb4f387e0617ef39721685', + 'bbc26fa1ff8c9f841d4f4758cccac1def0f9929c30c949451d4e71e4ded0a681', + '4ac05d0ec2e247aad1065f712d3d6934938e4709f224a0466f558bdf2e4cbf9c', + ].map(Buffer.from), + } + + testSingleProofGeneration(100, 'ff', 97, expected, options) + }) + + it('should generate a compact Single Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000064', + 'eb98df4415ff9a93976bb26b84f3819662fe31939e022cfa52d9061de351f6d5', + '06f8f83483a72750b8ba34cbe8fd54cc1243479b12f7b659075311dc54800203', + 'bbc26fa1ff8c9f841d4f4758cccac1def0f9929c30c949451d4e71e4ded0a681', + '4ac05d0ec2e247aad1065f712d3d6934938e4709f224a0466f558bdf2e4cbf9c', + ].map(Buffer.from), + } + + testSingleProofGeneration(100, 'ff', 97, expected, options) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should generate the same Single Proof for a 8-element Merkle Tree.', () => { + const balancedOptions = { unbalanced: false, sortedHash: false, compact: false } + const unbalancedOptions = { unbalanced: true, sortedHash: false, compact: false } + compareSingleProofs(8, 2, balancedOptions, unbalancedOptions) + }) + + it('should generate the same Compact Single Proof for a sorted-hash 8-element Merkle Tree.', () => { + const balancedOptions = { unbalanced: false, sortedHash: true, compact: true } + const unbalancedOptions = { unbalanced: true, sortedHash: true, compact: true } + compareSingleProofs(8, 2, balancedOptions, unbalancedOptions) + }) + }) + }) + + describe('Single Proof Verification', () => { + describe('Balanced', () => { + it('should verify a Single Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + testSingleProofVerification(8, 2, options) + }) + + it('should verify a Single Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + testSingleProofVerification(1, 0, options) + }) + + it('should verify a Single Proof for a sorted-hash 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + testSingleProofVerification(8, 2, options) + }) + + it('should verify a Single Proof for a sorted-hash 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + testSingleProofVerification(1, 0, options) + }) + + it('should verify a compact Single Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + testSingleProofVerification(8, 2, options) + }) + + it('should verify a compact Single Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + testSingleProofVerification(1, 0, options) + }) + }) + + describe('Unbalanced', () => { + it('should verify a Single Proof for a 9-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleProofVerification(9, 8, options) + }) + + it('should verify a Single Proof for a 27-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleProofVerification(27, 25, options) + }) + + it('should verify a Single Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleProofVerification(100, 97, options) + }) + + it('should verify a Single Proof for a sorted-hash 9-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleProofVerification(9, 8, options) + }) + + it('should verify a Single Proof for a sorted-hash 27-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleProofVerification(27, 25, options) + }) + + it('should verify a Single Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleProofVerification(100, 97, options) + }) + + it('should verify a compact Single Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testSingleProofVerification(100, 97, options) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should verify a Single Proof for a 8-element Merkle Tree, built with the unbalanced option.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleProofVerification(8, 2, options) + }) + + it('should verify a Compact Single Proof for a sorted-hash 8-element Merkle Tree, built with the unbalanced option.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testSingleProofVerification(8, 2, options) + }) + }) + }) + + describe('Single Proof Update', () => { + describe('Balanced', () => { + it('should use a Single Proof for a 8-element Merkle Tree to update an element.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + testSingleUpdate(8, 2, options) + }) + + it('should use a Single Proof for a 1-element Merkle Tree to update an element.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + testSingleUpdate(1, 0, options) + }) + + it('should use a Single Proof for a sorted-hash 8-element Merkle Tree to update an element.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + testSingleUpdate(8, 2, options) + }) + + it('should use a Single Proof for a sorted-hash 1-element Merkle Tree to update an element.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + testSingleUpdate(1, 0, options) + }) + + it('should use a compact Single Proof for a 8-element Merkle Tree to update an element.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + testSingleUpdate(8, 2, options) + }) + + it('should use a compact Single Proof for a 1-element Merkle Tree to update an element.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + testSingleUpdate(1, 0, options) + }) + }) + + describe('Unbalanced', () => { + it('should verify a Single Proof for a 9-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleUpdate(9, 8, options) + }) + + it('should verify a Single Proof for a 27-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleUpdate(27, 25, options) + }) + + it('should verify a Single Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleUpdate(100, 97, options) + }) + + it('should verify a Single Proof for a sorted-hash 9-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleUpdate(9, 8, options) + }) + + it('should verify a Single Proof for a sorted-hash 27-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleUpdate(27, 25, options) + }) + + it('should verify a Single Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleUpdate(100, 97, options) + }) + + it('should verify a compact Single Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testSingleUpdate(100, 97, options) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should use a Single Proof for a 8-element Merkle Tree, built with the unbalanced option, to update an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleUpdate(8, 2, options) + }) + + it('should use a Compact Single Proof for a sorted-hash 8-element Merkle Tree, built with the unbalanced option, to update an element.', () => { + const options = { unbalanced: true, sortedHash: true, compact: true } + testSingleUpdate(8, 2, options) + }) + }) + }) + + describe('Single Proof Update Consecutive Uses', () => { + describe('Balanced', () => { + it('should use 100 Single Proofs for a 16-element Merkle Tree, to update an 100 elements consecutively.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + testConsecutiveSingleUpdate(100, 16, options) + }) + }) + + describe('Unbalanced', () => { + it('should use 100 Compact Single Proofs for a 25-element sorted-hash Merkle Tree, to update an 100 elements consecutively.', () => { + const options = { unbalanced: true, sortedHash: true, compact: true } + testConsecutiveSingleUpdate(100, 25, options) + }) + }) + }) + }) + + describe('Index and Existence Multi Proofs', () => { + describe('Index and Existence Multi Proof Generation', () => { + describe('Balanced', () => { + it('should generate a Multi Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + + const expected = { + decommitments: [ + '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60', + '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39', + 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9', + ].map(Buffer.from), + } + + testMultiProofGeneration(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should generate a Multi Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + + const expected = { + decommitments: [], + } + + testMultiProofGeneration(1, 'ff', [0], expected, options) + }) + + it('should generate a Multi Proof for a sorted-hash 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: false } + + const expected = { + decommitments: [ + '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60', + 'df00f936e8f696ef3929c73d2176f2012336b0fd4fa5ae504bb3053a44993b94', + 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9', + ].map(Buffer.from), + } + + testMultiProofGeneration(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should generate a Multi Proof for a sorted-hash 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: false } + + const expected = { + decommitments: [], + } + + testMultiProofGeneration(1, 'ff', [0], expected, options) + }) + + it('should generate a compact Multi Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000008', + '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60', + '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39', + 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9', + ].map(Buffer.from), + } + + testMultiProofGeneration(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should generate a compact Multi Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: true } + + const expected = { + compactProof: ['0000000000000000000000000000000000000000000000000000000000000001'].map(Buffer.from), + } + + testMultiProofGeneration(1, 'ff', [0], expected, options) + }) + }) + + describe('Unbalanced', () => { + it('should generate a Multi Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + + const expected = { + decommitments: [ + '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964', + 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d', + ].map(Buffer.from), + } + + testMultiProofGeneration(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should generate a Multi Proof for a 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + + const expected = { + decommitments: [ + '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9', + '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec', + 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f', + '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d', + 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e', + 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445', + 'd1c7a0741902e7210c324ee399555decfb24a0281c42d0a690a84acae5a1cfd2', + '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2', + '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + ].map(Buffer.from), + } + + testMultiProofGeneration(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + + it('should generate a Multi Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: false } + + const expected = { + decommitments: [ + '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964', + 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + 'bc481a454a66b25fd1adf8b6b88cbcac3783d39d5ab1e4c45d114846da10274c', + ].map(Buffer.from), + } + + testMultiProofGeneration(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should generate a Multi Proof for a sorted-hash 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: false } + + const expected = { + decommitments: [ + '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9', + '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec', + 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f', + '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d', + 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e', + 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445', + 'c83c0742945e25d8ea750b433deb383bd3c68c5e415398cb3a1bf7ebd760fe85', + '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2', + 'df00f936e8f696ef3929c73d2176f2012336b0fd4fa5ae504bb3053a44993b94', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + ].map(Buffer.from), + } + + testMultiProofGeneration(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + + it('should generate a Compact Multi Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: true } + + const expected = { + compactProof: [ + '000000000000000000000000000000000000000000000000000000000000000c', + '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964', + 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d', + ].map(Buffer.from), + } + + testMultiProofGeneration(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should generate a Compact Multi Proof for a 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000013', + '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9', + '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec', + 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f', + '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d', + 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e', + 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445', + 'd1c7a0741902e7210c324ee399555decfb24a0281c42d0a690a84acae5a1cfd2', + '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2', + '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + ].map(Buffer.from), + } + + testMultiProofGeneration(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should generate the same Multi Proof for a 8-element Merkle Tree.', () => { + const balancedOptions = { unbalanced: false, sortedHash: false, indexed: true } + const unbalancedOptions = { unbalanced: true, sortedHash: false, indexed: true } + compareMultiProofs(8, [1, 4, 5], balancedOptions, unbalancedOptions) + }) + + it('should generate the same Multi Proof for a sorted-hash 8-element Merkle Tree.', () => { + const balancedOptions = { unbalanced: false, sortedHash: true, indexed: true } + const unbalancedOptions = { unbalanced: true, sortedHash: true, indexed: true } + compareMultiProofs(8, [1, 4, 5], balancedOptions, unbalancedOptions) + }) + }) + }) + + describe('Index and Existence Multi Proof Verification', () => { + describe('Balanced', () => { + it('should verify a Multi Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + testMultiProofVerification(8, [1, 4, 5], options) + }) + + it('should verify a Multi Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + testMultiProofVerification(1, [0], options) + }) + + it('should verify a Multi Proof for a sorted-hash 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: false } + testMultiProofVerification(8, [1, 4, 5], options) + }) + + it('should verify a Multi Proof for a sorted-hash 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: false } + testMultiProofVerification(1, [0], options) + }) + + it('should verify a compact Multi Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: true } + testMultiProofVerification(8, [1, 4, 5], options) + }) + + it('should verify a compact Multi Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: true } + testMultiProofVerification(1, [0], options) + }) + }) + + describe('Unbalanced', () => { + it('should verify a Multi Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + testMultiProofVerification(12, [2, 3, 8, 11], options) + }) + + it('should verify a Multi Proof for a 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + testMultiProofVerification(19, [2, 4, 9, 12, 17], options) + }) + + it('should verify a Multi Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: false } + testMultiProofVerification(12, [2, 3, 8, 11], options) + }) + + it('should verify a Multi Proof for a sorted-hash 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: false } + testMultiProofVerification(19, [2, 4, 9, 12, 17], options) + }) + + it('should verify a Compact Multi Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: true } + testMultiProofVerification(12, [2, 3, 8, 11], options) + }) + + it('should verify a Compact Multi Proof for a 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: true } + testMultiProofVerification(19, [2, 4, 9, 12, 17], options) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should verify a Multi Proof for a 8-element Merkle Tree, built with the unbalanced option.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true } + testMultiProofVerification(8, [1, 4, 5], options) + }) + + it('should verify a Multi Proof for a sorted-hash 8-element Merkle Tree, built with the unbalanced option.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true } + testMultiProofVerification(8, [1, 4, 5], options) + }) + }) + }) + + describe('Index and Existence Multi Proof Update', () => { + describe('Balanced', () => { + it('should use a Multi Proof for a 8-element Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + testMultiUpdate(8, [1, 4, 5], options) + }) + + it('should use a Multi Proof for a 1-element Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + testMultiUpdate(1, [0], options) + }) + + it('should use a Multi Proof for a sorted-hash 8-element Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: false } + testMultiUpdate(8, [1, 4, 5], options) + }) + + it('should use a Multi Proof for a sorted-hash 1-element Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: false } + testMultiUpdate(1, [0], options) + }) + + it('should use a compact Multi Proof for a 8-element Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: true } + testMultiUpdate(8, [1, 4, 5], options) + }) + + it('should use a compact Multi Proof for a 1-element Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: true } + testMultiUpdate(1, [0], options) + }) + }) + + describe('Unbalanced', () => { + it('should use a Multi Proof for a 12-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + testMultiUpdate(12, [2, 3, 8, 11], options) + }) + + it('should use a Multi Proof for a 19-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + testMultiUpdate(19, [2, 4, 9, 12, 17], options) + }) + + it('should use a Multi Proof for a sorted-hash 12-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: false } + testMultiUpdate(12, [2, 3, 8, 11], options) + }) + + it('should use a Multi Proof for a sorted-hash 19-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: false } + testMultiUpdate(19, [2, 4, 9, 12, 17], options) + }) + + it('should use a Compact Multi Proof for a 12-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: true } + testMultiUpdate(12, [2, 3, 8, 11], options) + }) + + it('should use a Compact Multi Proof for a 19-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: true } + testMultiUpdate(19, [2, 4, 9, 12, 17], options) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should use a Multi Proof for a 8-element Merkle Tree, built with the unbalanced option, to update an element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true } + testMultiUpdate(8, [1, 4, 5], options) + }) + + it('should use a Multi Proof for a sorted-hash 8-element Merkle Tree, built with the unbalanced option, to update an element.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true } + testMultiUpdate(8, [1, 4, 5], options) + }) + }) + }) + + describe('Index and Existence Multi Proof Update Consecutive Uses', () => { + describe('Balanced', () => { + it('should use 100 Multi Proofs for a 16-element Merkle Tree, to perform 100 updates of up to 6 random elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + testConsecutiveMultiUpdate(100, 16, 6, options) + }) + + it('should use 100 Compact Multi Proofs for a 16-element sorted-hash Merkle Tree, to perform 100 updates of up to 6 random elements.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: true } + testConsecutiveMultiUpdate(100, 16, 6, options) + }) + }) + + describe('Unbalanced', () => { + it('should use 100 Multi Proofs for a 19-element Merkle Tree, to perform 100 updates of up to 6 random elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + testConsecutiveMultiUpdate(100, 19, 6, options) + }) + + it('should use 100 Compact Multi Proofs for a sorted-hash 19-element Merkle Tree, to perform 100 updates of up to 6 random elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: true } + testConsecutiveMultiUpdate(100, 19, 6, options) + }) + }) + }) + }) + + describe('Existence-Only Boolean-Array Multi Proofs', () => { + describe('Existence-Only Boolean-Array Multi Proof Generation', () => { + describe('Balanced', () => { + it('should generate a Multi Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + + const expected = { + decommitments: [ + '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60', + '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39', + 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9', + ].map(Buffer.from), + flags: [true, false, false, false, true].map(e => e ? 1 : 0), + skips: [false, false, false, false, false].map(e => e ? 1 : 0), + orders: [true, false, true, true, true].map(e => e ? 1 : 0), + } + + testMultiProofGeneration(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should generate a Multi Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + + const expected = { + decommitments: [], + flags: [], + skips: [], + orders: [], + } + + testMultiProofGeneration(1, 'ff', [0], expected, options) + }) + + it('should generate a Multi Proof for a sorted-hash 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: false } + + const expected = { + decommitments: [ + '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60', + 'df00f936e8f696ef3929c73d2176f2012336b0fd4fa5ae504bb3053a44993b94', + 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9', + ].map(Buffer.from), + flags: [true, false, false, false, true].map(e => e ? 1 : 0), + skips: [false, false, false, false, false].map(e => e ? 1 : 0), + } + + testMultiProofGeneration(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should generate a Multi Proof for a sorted-hash 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: false } + + const expected = { + decommitments: [], + flags: [], + skips: [], + } + + testMultiProofGeneration(1, 'ff', [0], expected, options) + }) + }) + + describe('Unbalanced', () => { + it('should generate a Multi Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + + const expected = { + decommitments: [ + '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964', + 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d', + ].map(Buffer.from), + flags: [false, false, true, true, false, false, false, true].map(e => e ? 1 : 0), + skips: [false, false, false, false, false, true, false, false].map(e => e ? 1 : 0), + orders: [false, true, true, true, false, true, true, true].map(e => e ? 1 : 0), + } + + testMultiProofGeneration(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should generate a Multi Proof for a 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + + const expected = { + decommitments: [ + '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9', + '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec', + 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f', + '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d', + 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e', + 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445', + 'd1c7a0741902e7210c324ee399555decfb24a0281c42d0a690a84acae5a1cfd2', + '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2', + '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + ].map(Buffer.from), + flags: [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + true, + ].map(e => e ? 1 : 0), + skips: [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + ].map(e => e ? 1 : 0), + orders: [false, true, false, true, true, true, true, true, true, false, true, true, true, true, true, true].map(e => e ? 1 : 0), + } + + testMultiProofGeneration(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + + it('should generate a Multi Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + + const expected = { + decommitments: [ + '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964', + 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + 'bc481a454a66b25fd1adf8b6b88cbcac3783d39d5ab1e4c45d114846da10274c', + ].map(Buffer.from), + flags: [false, false, true, true, false, false, false, true].map(e => e ? 1 : 0), + skips: [false, false, false, false, false, true, false, false].map(e => e ? 1 : 0), + } + + testMultiProofGeneration(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should generate a Multi Proof for a sorted-hash 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + + const expected = { + decommitments: [ + '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9', + '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec', + 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f', + '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d', + 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e', + 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445', + 'c83c0742945e25d8ea750b433deb383bd3c68c5e415398cb3a1bf7ebd760fe85', + '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2', + 'df00f936e8f696ef3929c73d2176f2012336b0fd4fa5ae504bb3053a44993b94', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + ].map(Buffer.from), + flags: [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + true, + ].map(e => e ? 1 : 0), + skips: [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + ].map(e => e ? 1 : 0), + } + + testMultiProofGeneration(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should generate the same Multi Proof for a 8-element Merkle Tree.', () => { + const balancedOptions = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + const unbalancedOptions = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + compareMultiProofs(8, [1, 4, 5], balancedOptions, unbalancedOptions) + }) + + it('should generate the same Multi Proof for a sorted-hash 8-element Merkle Tree.', () => { + const balancedOptions = { unbalanced: false, sortedHash: true, indexed: false, compact: false } + const unbalancedOptions = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + compareMultiProofs(8, [1, 4, 5], balancedOptions, unbalancedOptions) + }) + }) + }) + + describe('Existence-Only Boolean-Array Multi Proof Verification', () => { + describe('Balanced', () => { + it('should verify a Multi Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + testMultiProofVerification(8, [1, 4, 5], options) + }) + + it('should verify a Multi Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + testMultiProofVerification(1, [0], options) + }) + + it('should verify a Multi Proof for a sorted-hash 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: false } + testMultiProofVerification(8, [1, 4, 5], options) + }) + + it('should verify a Multi Proof for a sorted-hash 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: false } + testMultiProofVerification(1, [0], options) + }) + }) + + describe('Unbalanced', () => { + it('should verify a Multi Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiProofVerification(12, [2, 3, 8, 11], options) + }) + + it('should verify a Multi Proof for a 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiProofVerification(19, [2, 4, 9, 12, 17], options) + }) + + it('should verify a Multi Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiProofVerification(12, [2, 3, 8, 11], options) + }) + + it('should verify a Multi Proof for a sorted-hash 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiProofVerification(19, [2, 4, 9, 12, 17], options) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should verify a Multi Proof for a 8-element Merkle Tree, built with the unbalanced option.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiProofVerification(8, [1, 4, 5], options) + }) + + it('should verify a Multi Proof for a sorted-hash 8-element Merkle Tree, built with the unbalanced option.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiProofVerification(8, [1, 4, 5], options) + }) + }) + }) + + describe('Existence-Only Boolean-Array Multi Indices Inferring', () => { + describe('Balanced', () => { + it('should verify a Multi Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + testMultiProofIndicesInferring(1, [0], options) + }) + + it('should verify a Multi Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + testMultiProofIndicesInferring(8, [1, 4, 5], options) + }) + + it('should verify a Multi Proof for a 64-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + testMultiProofIndicesInferring(64, [0, 1, 7, 13, 15, 26, 34, 35, 36, 50, 62], options) + }) + }) + + describe('Unbalanced', () => { + it('should verify a Multi Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiProofIndicesInferring(12, [2, 3, 8, 11], options) + }) + + it('should verify a Multi Proof for a 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiProofIndicesInferring(19, [2, 4, 9, 12, 17], options) + }) + + it('should verify a Multi Proof for a 85-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiProofIndicesInferring(85, [5, 6, 20, 36, 37, 65, 78, 83], options) + }) + }) + }) + + describe('Existence-Only Boolean-Array Multi Proof Update', () => { + describe('Balanced', () => { + it('should use a Multi Proof for a 8-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + testMultiUpdate(8, [1, 4, 5], options) + }) + + it('should use a Multi Proof for a 1-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + testMultiUpdate(1, [0], options) + }) + + it('should use a Multi Proof for a sorted-hash 8-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: false } + testMultiUpdate(8, [1, 4, 5], options) + }) + + it('should use a Multi Proof for a sorted-hash 1-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: false } + testMultiUpdate(1, [0], options) + }) + }) + + describe('Unbalanced', () => { + it('should use a Multi Proof for a 12-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdate(12, [2, 3, 8, 11], options) + }) + + it('should use a Multi Proof for a 19-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdate(19, [2, 4, 9, 12, 17], options) + }) + + it('should use a Multi Proof for a sorted-hash 12-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdate(12, [2, 3, 8, 11], options) + }) + + it('should use a Multi Proof for a sorted-hash 19-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdate(19, [2, 4, 9, 12, 17], options) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should use a Multi Proof for a 8-element Merkle Tree, built with the unbalanced option, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdate(8, [1, 4, 5], options) + }) + + it('should use a Multi Proof for a sorted-hash 8-element Merkle Tree, built with the unbalanced option, to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdate(8, [1, 4, 5], options) + }) + }) + }) + + describe('Existence-Only Boolean-Array Multi Proof Update Consecutive Uses', () => { + describe('Balanced', () => { + it('should use 100 Multi Proofs for a 16-element Merkle Tree, to perform 100 updates of up to 6 random elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + testConsecutiveMultiUpdate(100, 16, 6, options) + }) + + it('should use 100 Multi Proofs for a sorted-hash 16-element Merkle Tree, to perform 100 updates of up to 6 random elements.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: false } + testConsecutiveMultiUpdate(100, 16, 6, options) + }) + }) + + describe('Unbalanced', () => { + it('should use 100 Multi Proofs for a 19-element Merkle Tree, to perform 100 updates of up to 6 random elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testConsecutiveMultiUpdate(100, 19, 6, options) + }) + + it('should use 100 Multi Proofs for a sorted-hash 19-element Merkle Tree, to perform 100 updates of up to 6 random elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testConsecutiveMultiUpdate(100, 19, 6, options) + }) + }) + }) + }) + + describe('Existence-Only Boolean-Bit (Compact) Multi Proofs', () => { + describe('Existence-Only Boolean-Bit Multi Proof Generation', () => { + describe('Balanced', () => { + it('should generate a compact Multi Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000008', + '0000000000000000000000000000000000000000000000000000000000000031', + '0000000000000000000000000000000000000000000000000000000000000020', + '000000000000000000000000000000000000000000000000000000000000001d', + '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60', + '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39', + 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9', + ].map(Buffer.from), + } + + testMultiProofGeneration(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should generate a compact Multi Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000001', + '0000000000000000000000000000000000000000000000000000000000000001', + '0000000000000000000000000000000000000000000000000000000000000001', + '0000000000000000000000000000000000000000000000000000000000000000', + ].map(Buffer.from), + } + + testMultiProofGeneration(1, 'ff', [0], expected, options) + }) + + it('should generate a compact Multi Proof for a sorted-hash 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000008', + '0000000000000000000000000000000000000000000000000000000000000031', + '0000000000000000000000000000000000000000000000000000000000000020', + '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60', + 'df00f936e8f696ef3929c73d2176f2012336b0fd4fa5ae504bb3053a44993b94', + 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9', + ].map(Buffer.from), + } + + testMultiProofGeneration(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should generate a compact Multi Proof for a sorted-hash 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000001', + '0000000000000000000000000000000000000000000000000000000000000001', + '0000000000000000000000000000000000000000000000000000000000000001', + ].map(Buffer.from), + } + + testMultiProofGeneration(1, 'ff', [0], expected, options) + }) + }) + + describe('Unbalanced', () => { + it('should generate a compact Multi Proof for a 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000003', + '000000000000000000000000000000000000000000000000000000000000000e', + '0000000000000000000000000000000000000000000000000000000000000009', + '0000000000000000000000000000000000000000000000000000000000000007', + ].map(Buffer.from), + } + + testMultiProofGeneration(3, 'ff', [0, 1, 2], expected, options) + }) + + it('should generate a compact Multi Proof for a 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000003', + '000000000000000000000000000000000000000000000000000000000000000c', + '0000000000000000000000000000000000000000000000000000000000000009', + '0000000000000000000000000000000000000000000000000000000000000005', + '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60', + ].map(Buffer.from), + } + + testMultiProofGeneration(3, 'ff', [1, 2], expected, options) + }) + + it('should generate a compact Multi Proof for a 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000003', + '0000000000000000000000000000000000000000000000000000000000000004', + '0000000000000000000000000000000000000000000000000000000000000005', + '0000000000000000000000000000000000000000000000000000000000000001', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + ].map(Buffer.from), + } + + testMultiProofGeneration(3, 'ff', [2], expected, options) + }) + + it('should generate a compact Multi Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + + const expected = { + compactProof: [ + '000000000000000000000000000000000000000000000000000000000000000c', + '000000000000000000000000000000000000000000000000000000000000018c', + '0000000000000000000000000000000000000000000000000000000000000120', + '00000000000000000000000000000000000000000000000000000000000000ee', + '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964', + 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d', + ].map(Buffer.from), + } + + testMultiProofGeneration(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should generate a compact Multi Proof for a 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000013', + '000000000000000000000000000000000000000000000000000000000001d800', + '0000000000000000000000000000000000000000000000000000000000012400', + '000000000000000000000000000000000000000000000000000000000000fdfa', + '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9', + '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec', + 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f', + '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d', + 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e', + 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445', + 'd1c7a0741902e7210c324ee399555decfb24a0281c42d0a690a84acae5a1cfd2', + '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2', + '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + ].map(Buffer.from), + } + + testMultiProofGeneration(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + + it('should generate a compact Multi Proof for a sorted-hash 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000003', + '000000000000000000000000000000000000000000000000000000000000000e', + '0000000000000000000000000000000000000000000000000000000000000009', + ].map(Buffer.from), + } + + testMultiProofGeneration(3, 'ff', [0, 1, 2], expected, options) + }) + + it('should generate a compact Multi Proof for a sorted-hash 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000003', + '000000000000000000000000000000000000000000000000000000000000000c', + '0000000000000000000000000000000000000000000000000000000000000009', + '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60', + ].map(Buffer.from), + } + + testMultiProofGeneration(3, 'ff', [1, 2], expected, options) + }) + + it('should generate a compact Multi Proof for a sorted-hash 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000003', + '0000000000000000000000000000000000000000000000000000000000000004', + '0000000000000000000000000000000000000000000000000000000000000005', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + ].map(Buffer.from), + } + + testMultiProofGeneration(3, 'ff', [2], expected, options) + }) + + it('should generate a compact Multi Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + const expected = { + compactProof: [ + '000000000000000000000000000000000000000000000000000000000000000c', + '000000000000000000000000000000000000000000000000000000000000018c', + '0000000000000000000000000000000000000000000000000000000000000120', + '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964', + 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + 'bc481a454a66b25fd1adf8b6b88cbcac3783d39d5ab1e4c45d114846da10274c', + ].map(Buffer.from), + } + + testMultiProofGeneration(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should generate a compact Multi Proof for a sorted-hash 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000013', + '000000000000000000000000000000000000000000000000000000000001d800', + '0000000000000000000000000000000000000000000000000000000000012400', + '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9', + '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec', + 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f', + '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d', + 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e', + 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445', + 'c83c0742945e25d8ea750b433deb383bd3c68c5e415398cb3a1bf7ebd760fe85', + '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2', + 'df00f936e8f696ef3929c73d2176f2012336b0fd4fa5ae504bb3053a44993b94', + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + ].map(Buffer.from), + } + + testMultiProofGeneration(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should generate the same Multi Proof for a 8-element Merkle Tree.', () => { + const balancedOptions = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + const unbalancedOptions = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + compareMultiProofs(8, [1, 4, 5], balancedOptions, unbalancedOptions) + }) + + it('should generate the same Multi Proof for a sorted-hash 8-element Merkle Tree.', () => { + const balancedOptions = { unbalanced: false, sortedHash: true, indexed: false, compact: true } + const unbalancedOptions = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + compareMultiProofs(8, [1, 4, 5], balancedOptions, unbalancedOptions) + }) + }) + }) + + describe('Existence-Only Boolean-Bit Multi Proof Verification', () => { + describe('Balanced', () => { + it('should verify a Multi Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + testMultiProofVerification(8, [1, 4, 5], options) + }) + + it('should verify a Multi Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + testMultiProofVerification(1, [0], options) + }) + + it('should verify a Multi Proof for a sorted-hash 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: true } + testMultiProofVerification(8, [1, 4, 5], options) + }) + + it('should verify a Multi Proof for a sorted-hash 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: true } + testMultiProofVerification(1, [0], options) + }) + }) + + describe('Unbalanced', () => { + it('should verify a Multi Proof for a 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiProofVerification(3, [0, 1, 2], options) + }) + + it('should verify a Multi Proof for a 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiProofVerification(3, [1, 2], options) + }) + + it('should verify a Multi Proof for a 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiProofVerification(3, [2], options) + }) + + it('should verify a Multi Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiProofVerification(12, [2, 3, 8, 11], options) + }) + + it('should verify a Multi Proof for a 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiProofVerification(19, [2, 4, 9, 12, 17], options) + }) + + it('should verify a Multi Proof for a sorted-hash 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiProofVerification(3, [0, 1, 2], options) + }) + + it('should verify a Multi Proof for a sorted-hash 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiProofVerification(3, [1, 2], options) + }) + + it('should verify a Multi Proof for a sorted-hash 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiProofVerification(3, [2], options) + }) + + it('should verify a Multi Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiProofVerification(12, [2, 3, 8, 11], options) + }) + + it('should verify a Multi Proof for a sorted-hash 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiProofVerification(19, [2, 4, 9, 12, 17], options) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should verify a Multi Proof for a 8-element Merkle Tree, built with the unbalanced option.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiProofVerification(8, [1, 4, 5], options) + }) + + it('should verify a Multi Proof for a sorted-hash 8-element Merkle Tree, built with the unbalanced option.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiProofVerification(8, [1, 4, 5], options) + }) + }) + }) + + describe('Existence-Only Boolean-Bit Multi Indices Inferring', () => { + describe('Balanced', () => { + it('should verify a Multi Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + testMultiProofIndicesInferring(1, [0], options) + }) + + it('should verify a Multi Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + testMultiProofIndicesInferring(8, [1, 4, 5], options) + }) + + it('should verify a Multi Proof for a 64-element Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + testMultiProofIndicesInferring(64, [0, 1, 7, 13, 15, 26, 34, 35, 36, 50, 62], options) + }) + }) + + describe('Unbalanced', () => { + it('should verify a Multi Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiProofIndicesInferring(12, [2, 3, 8, 11], options) + }) + + it('should verify a Multi Proof for a 19-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiProofIndicesInferring(19, [2, 4, 9, 12, 17], options) + }) + + it('should verify a Multi Proof for a 85-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiProofIndicesInferring(85, [5, 6, 20, 36, 37, 65, 78, 83], options) + }) + }) + }) + + describe('Existence-Only Boolean-Bit Multi Proof Update', () => { + describe('Balanced', () => { + it('should use a Multi Proof for a 8-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + testMultiUpdate(8, [1, 4, 5], options) + }) + + it('should use a Multi Proof for a 1-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + testMultiUpdate(1, [0], options) + }) + + it('should use a Multi Proof for a sorted-hash 8-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: true } + testMultiUpdate(8, [1, 4, 5], options) + }) + + it('should use a Multi Proof for a sorted-hash 1-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: true } + testMultiUpdate(1, [0], options) + }) + }) + + describe('Unbalanced', () => { + it('should use a Multi Proof for a 3-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdate(3, [0, 1, 2], options) + }) + + it('should use a Multi Proof for a 3-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdate(3, [1, 2], options) + }) + + it('should use a Multi Proof for a 3-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdate(3, [2], options) + }) + + it('should use a Multi Proof for a 12-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdate(12, [2, 3, 8, 11], options) + }) + + it('should use a Multi Proof for a 19-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdate(19, [2, 4, 9, 12, 17], options) + }) + + it('should use a Multi Proof for a sorted-hash 3-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdate(3, [0, 1, 2], options) + }) + + it('should use a Multi Proof for a sorted-hash 3-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdate(3, [1, 2], options) + }) + + it('should use a Multi Proof for a sorted-hash 3-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdate(3, [2], options) + }) + + it('should use a Multi Proof for a sorted-hash 12-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdate(12, [2, 3, 8, 11], options) + }) + + it('should use a Multi Proof for a sorted-hash 19-element Merkle Tree, to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdate(19, [2, 4, 9, 12, 17], options) + }) + }) + + describe('Balanced/Unbalanced Overlapping Cases', () => { + it('should use a Multi Proof for a 8-element Merkle Tree, built with the unbalanced option, to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdate(8, [1, 4, 5], options) + }) + + it('should use a Multi Proof for a sorted-hash 8-element Merkle Tree, built with the unbalanced option, to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdate(8, [1, 4, 5], options) + }) + }) + }) + + describe('Existence-Only Boolean-Bit Multi Proof Update Consecutive Uses', () => { + describe('Balanced', () => { + it('should use 100 Multi Proofs for a 16-element Merkle Tree, to perform 100 updates of up to 6 random elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + testConsecutiveMultiUpdate(100, 16, 6, options) + }) + + it('should use 100 Multi Proofs for a sorted-hash 16-element Merkle Tree, to perform 100 updates of up to 6 random elements.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: false, compact: true } + testConsecutiveMultiUpdate(100, 16, 6, options) + }) + }) + + describe('Unbalanced', () => { + it('should use 100 Multi Proofs for a 19-element Merkle Tree, to perform 100 updates of up to 6 random elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testConsecutiveMultiUpdate(100, 19, 6, options) + }) + + it('should use 50 Multi Proofs for a 89-element Merkle Tree, to perform 50 updates of up to 13 random elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testConsecutiveMultiUpdate(50, 89, 13, options) + }) + + it('should use 100 Multi Proofs for a sorted-hash 19-element Merkle Tree, to perform 100 updates of up to 6 random elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testConsecutiveMultiUpdate(100, 19, 6, options) + }) + + it('should use 50 Multi Proofs for a sorted-hash 89-element Merkle Tree, to perform 50 updates of up to 13 random elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testConsecutiveMultiUpdate(50, 89, 13, options) + }) + }) + }) + }) + + describe('Append Proofs', () => { + describe('Append Proof Generation', () => { + it('should generate an Append Proof for a 0-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const expected = { + decommitments: [], + } + + testAppendProofGeneration(0, 'ff', expected, options) + }) + + it('should generate an Append Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const expected = { + decommitments: ['0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60'].map(Buffer.from), + } + + testAppendProofGeneration(1, 'ff', expected, options) + }) + + it('should generate an Append Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const expected = { + decommitments: ['a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27'].map(Buffer.from), + } + + testAppendProofGeneration(2, 'ff', expected, options) + }) + + it('should generate an Append Proof for a 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const expected = { + decommitments: [ + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b', + ].map(Buffer.from), + } + + testAppendProofGeneration(3, 'ff', expected, options) + }) + + it('should generate an Append Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const expected = { + decommitments: ['0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4'].map(Buffer.from), + } + + testAppendProofGeneration(8, 'ff', expected, options) + }) + + it('should generate an Append Proof for a 15-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const expected = { + decommitments: [ + '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4', + 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b', + '712ed55abe1946b941876a6230b3135edadc400a18897e029ffdbff6900503e6', + 'e481ff292c1b323f27dd2e7b0da511947e0d349a0616a739ea628a3a5888c529', + ].map(Buffer.from), + } + + testAppendProofGeneration(15, 'ff', expected, options) + }) + + it('should generate an Append Proof for a 20-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const expected = { + decommitments: [ + 'c7ec3e428ae2869b12c1b8e12a84e56f0d7f3cbe752cd1c3775158cf846412be', + 'febc2d558e22b7e32db3a5dd0b4d8ac3dac5835493955c53e3eb0f8fdb2f4954', + ].map(Buffer.from), + } + + testAppendProofGeneration(20, 'ff', expected, options) + }) + + it('should generate an Append Proof for a 20-element sorted-hash Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + + const expected = { + decommitments: [ + '2c2cdc952c9d537709959cd357f6268fff33e5e21147f1a23db6cae78fb91eb9', + 'b1b7aca080829feceb8c4da79c5552b533f4f1542667ca689217d04230f835b0', + ].map(Buffer.from), + } + + testAppendProofGeneration(20, 'ff', expected, options) + }) + + it('should generate a compact Append Proof for a 0-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + + const expected = { + compactProof: ['0000000000000000000000000000000000000000000000000000000000000000'].map(Buffer.from), + } + + testAppendProofGeneration(0, 'ff', expected, options) + }) + + it('should generate a compact Append Proof for a 20-element sorted-hash Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000014', + '2c2cdc952c9d537709959cd357f6268fff33e5e21147f1a23db6cae78fb91eb9', + 'b1b7aca080829feceb8c4da79c5552b533f4f1542667ca689217d04230f835b0', + ].map(Buffer.from), + } + + testAppendProofGeneration(20, 'ff', expected, options) + }) + + it('should generate a compact Append Proof for a 20-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + + const expected = { + compactProof: [ + '0000000000000000000000000000000000000000000000000000000000000014', + 'c7ec3e428ae2869b12c1b8e12a84e56f0d7f3cbe752cd1c3775158cf846412be', + 'febc2d558e22b7e32db3a5dd0b4d8ac3dac5835493955c53e3eb0f8fdb2f4954', + ].map(Buffer.from), + } + + testAppendProofGeneration(20, 'ff', expected, options) + }) + }) + + describe('Append Proof Verification', () => { + it('should verify an Append Proof for a 0-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendProofVerification(0, options) + }) + + it('should verify an Append Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendProofVerification(1, options) + }) + + it('should verify an Append Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendProofVerification(2, options) + }) + + it('should verify an Append Proof for a 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendProofVerification(3, options) + }) + + it('should verify an Append Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendProofVerification(8, options) + }) + + it('should verify an Append Proof for a 15-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendProofVerification(15, options) + }) + + it('should verify an Append Proof for a 20-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendProofVerification(20, options) + }) + + it('should verify an Append Proof for a 0-element sorted-hash Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testAppendProofVerification(0, options) + }) + + it('should verify an Append Proof for a 1-element sorted-hash Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testAppendProofVerification(1, options) + }) + + it('should verify an Append Proof for a 2-element sorted-hash Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testAppendProofVerification(2, options) + }) + + it('should verify an Append Proof for a 3-element sorted-hash Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testAppendProofVerification(3, options) + }) + + it('should verify an Append Proof for a 8-element sorted-hash Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testAppendProofVerification(8, options) + }) + + it('should verify an Append Proof for a 15-element sorted-hash Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testAppendProofVerification(15, options) + }) + + it('should verify an Append Proof for a 20-element sorted-hash Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testAppendProofVerification(20, options) + }) + + it('should verify a compact Append Proof for a 0-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testAppendProofVerification(0, options) + }) + + it('should verify a compact Append Proof for a 20-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testAppendProofVerification(20, options) + }) + }) + + describe('Append Proof Single Append', () => { + it('should use an Append Proof for a 0-element Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleAppend(0, options) + }) + + it('should use an Append Proof for a 1-element Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleAppend(1, options) + }) + + it('should use an Append Proof for a 2-element Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleAppend(2, options) + }) + + it('should use an Append Proof for a 3-element Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleAppend(3, options) + }) + + it('should use an Append Proof for a 8-element Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleAppend(8, options) + }) + + it('should use an Append Proof for a 15-element Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleAppend(15, options) + }) + + it('should use an Append Proof for a 20-element Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSingleAppend(20, options) + }) + + it('should use an Append Proof for a 0-element sorted-hash Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleAppend(0, options) + }) + + it('should use an Append Proof for a 1-element sorted-hash Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleAppend(1, options) + }) + + it('should use an Append Proof for a 2-element sorted-hash Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleAppend(2, options) + }) + + it('should use an Append Proof for a 3-element sorted-hash Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleAppend(3, options) + }) + + it('should use an Append Proof for a 8-element sorted-hash Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleAppend(8, options) + }) + + it('should use an Append Proof for a 15-element sorted-hash Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleAppend(15, options) + }) + + it('should use an Append Proof for a 20-element sorted-hash Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testSingleAppend(20, options) + }) + + it('should use a compact Append Proof for a 0-element Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testSingleAppend(0, options) + }) + + it('should use a compact Append Proof for a 20-element Merkle Tree, to append an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testSingleAppend(20, options) + }) + }) + + describe('Append Proof Single Append Consecutive Uses', () => { + it('should use 100 Append Proofs for a 0-element Merkle Tree, to append an 100 elements consecutively.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testConsecutiveSingleAppend(100, 0, options) + }) + + it('should use 50 Compact Append Proofs for a 160-element Merkle Tree, to append an 50 elements consecutively.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testConsecutiveSingleAppend(50, 160, options) + }) + }) + + describe('Append Proof Multi Append', () => { + it('should use a Multi Append Proof for a 0-element Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testMultiAppend(0, 5, options) + }) + + it('should use a Multi Append Proof for a 1-element Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testMultiAppend(1, 5, options) + }) + + it('should use a Multi Append Proof for a 2-element Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testMultiAppend(2, 5, options) + }) + + it('should use a Multi Append Proof for a 3-element Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testMultiAppend(3, 5, options) + }) + + it('should use a Multi Append Proof for a 8-element Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testMultiAppend(8, 5, options) + }) + + it('should use a Multi Append Proof for a 15-element Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testMultiAppend(15, 5, options) + }) + + it('should use a Multi Append Proof for a 19-element Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testMultiAppend(19, 5, options) + }) + + it('should use a Multi Append Proof for a 20-element Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testMultiAppend(20, 5, options) + }) + + it('should use a Multi Append Proof for a 49-element Merkle Tree, to append 17 elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testMultiAppend(49, 17, options) + }) + + it('should use a Multi Append Proof for a 120-element Merkle Tree, to append 8 elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testMultiAppend(120, 8, options) + }) + + it('should use a Multi Append Proof for a 0-element sorted-hash Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testMultiAppend(0, 5, options) + }) + + it('should use a Multi Append Proof for a 1-element sorted-hash Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testMultiAppend(1, 5, options) + }) + + it('should use a Multi Append Proof for a 2-element sorted-hash Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testMultiAppend(2, 5, options) + }) + + it('should use a Multi Append Proof for a 3-element sorted-hash Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testMultiAppend(3, 5, options) + }) + + it('should use a Multi Append Proof for a 8-element sorted-hash Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testMultiAppend(8, 5, options) + }) + + it('should use a Multi Append Proof for a 15-element sorted-hash Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testMultiAppend(15, 5, options) + }) + + it('should use a Multi Append Proof for a sorted-hash 19-element Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testMultiAppend(19, 5, options) + }) + + it('should use a Multi Append Proof for a sorted-hash 20-element Merkle Tree, to append 5 elements.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testMultiAppend(20, 5, options) + }) + + it('should use a Multi Append Proof for a sorted-hash 49-element Merkle Tree, to append 17 elements.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testMultiAppend(49, 17, options) + }) + + it('should use a Multi Append Proof for a sorted-hash 120-element Merkle Tree, to append 8 elements.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testMultiAppend(120, 8, options) + }) + + it('should use a compact Multi Append Proof for a 0-element Merkle Tree, to append 8 elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testMultiAppend(0, 8, options) + }) + + it('should use a compact Multi Append Proof for a 120-element Merkle Tree, to append 8 elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testMultiAppend(120, 8, options) + }) + }) + + describe('Append Proof Multi Append Consecutive Uses', () => { + it('should use 100 Multi Append Proofs for a 0-element Merkle Tree, to perform 100 appends of up to 4 random elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testConsecutiveMultiAppend(100, 0, 4, options) + }) + + it('should use 25 Multi Append Proofs for a 1-element Merkle Tree, to perform 100 appends of up to 9 random elements.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testConsecutiveMultiAppend(25, 1, 9, options) + }) + + it('should use 50 Compact Multi Append Proofs for a 160-element sorted-hash Merkle Tree, to perform 50 appends of up to 3 random elements.', () => { + const options = { unbalanced: true, sortedHash: true, compact: true } + testConsecutiveMultiAppend(50, 160, 3, options) + }) + }) + }) + + describe('Combined Proof Common', () => { + describe('Get Minimum Element Index for Combined Proof', () => { + it('should get the minimum element index to be included in a Combined Proof a 1-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(1, { minimumIndex: 0 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 2-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(2, { minimumIndex: 0 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 3-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(3, { minimumIndex: 2 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 4-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(4, { minimumIndex: 0 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 5-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(5, { minimumIndex: 4 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 6-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(6, { minimumIndex: 4 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 7-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(7, { minimumIndex: 6 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 8-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(8, { minimumIndex: 0 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 23-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(23, { minimumIndex: 22 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 48-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(48, { minimumIndex: 32 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 365-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(365, { minimumIndex: 364 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 384-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(384, { minimumIndex: 256 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 580-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(580, { minimumIndex: 576 }, options) + }) + + it('should get the minimum element index to be included in a Combined Proof a 1792-element Merkle Tree.', () => { + const options = { unbalanced: true } + testCombinedProofMinimumIndex(1792, { minimumIndex: 1536 }, options) + }) + }) + + describe('Boolean-Array Combined Proof Verification (Single Update, Single Append)', () => { + it('should verify a Combined Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(1, [0], options) + }) + + it('should verify a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(2, [1], options) + }) + + it('should verify a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(128, [127], options) + }) + + it('should verify a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(100, [99], options) + }) + + it('should verify a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(101, [100], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(12, [11], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(12, [10], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(12, [8], options) + }) + }) + + describe('Boolean-Array Combined Proof Verification (Multi Update, Multi Append)', () => { + it('should verify a Combined Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(1, [0], options) + }) + + it('should verify a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(2, [1], options) + }) + + it('should verify a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(2, [0, 1], options) + }) + + it('should verify a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(128, [2, 4, 12, 15, 127], options) + }) + + it('should verify a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(128, [2, 4, 12, 15, 126, 127], options) + }) + + it('should verify a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(100, [99], options) + }) + + it('should verify a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(100, [2, 4, 12, 15, 99], options) + }) + + it('should verify a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(100, [2, 4, 12, 15, 98, 99], options) + }) + + it('should verify a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(100, [2, 4, 12, 15, 97, 99], options) + }) + + it('should verify a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(100, [2, 4, 12, 15, 97, 98, 99], options) + }) + + it('should verify a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(101, [100], options) + }) + + it('should verify a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(101, [2, 4, 12, 15, 100], options) + }) + + it('should verify a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(101, [2, 4, 12, 15, 99, 100], options) + }) + + it('should verify a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(101, [2, 4, 12, 15, 98, 100], options) + }) + + it('should verify a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(101, [2, 4, 12, 15, 98, 99, 100], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(12, [7, 11], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(12, [7, 10], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(12, [7, 9], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testCombinedProofVerification(12, [7, 8], options) + }) + + it('should verify a Combined Proof for a sorted-hash 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(1, [0], options) + }) + + it('should verify a Combined Proof for a sorted-hash 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(2, [1], options) + }) + + it('should verify a Combined Proof for a sorted-hash 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(2, [0, 1], options) + }) + + it('should verify a Combined Proof for a sorted-hash 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(128, [2, 4, 12, 15, 127], options) + }) + + it('should verify a Combined Proof for a sorted-hash 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(128, [2, 4, 12, 15, 126, 127], options) + }) + + it('should verify a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(100, [99], options) + }) + + it('should verify a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(100, [2, 4, 12, 15, 99], options) + }) + + it('should verify a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(100, [2, 4, 12, 15, 98, 99], options) + }) + + it('should verify a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(100, [2, 4, 12, 15, 97, 99], options) + }) + + it('should verify a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(100, [2, 4, 12, 15, 97, 98, 99], options) + }) + + it('should verify a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(101, [100], options) + }) + + it('should verify a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(101, [2, 4, 12, 15, 100], options) + }) + + it('should verify a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(101, [2, 4, 12, 15, 99, 100], options) + }) + + it('should verify a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(101, [2, 4, 12, 15, 98, 100], options) + }) + + it('should verify a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(101, [2, 4, 12, 15, 98, 99, 100], options) + }) + + it('should verify a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(12, [7, 11], options) + }) + + it('should verify a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(12, [7, 10], options) + }) + + it('should verify a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(12, [7, 9], options) + }) + + it('should verify a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testCombinedProofVerification(12, [7, 8], options) + }) + }) + + describe('Boolean-Bit Combined Proof Verification (Single Update, Single Append)', () => { + it('should verify a Combined Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(1, [0], options) + }) + + it('should verify a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(2, [1], options) + }) + + it('should verify a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(128, [127], options) + }) + + it('should verify a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(100, [99], options) + }) + + it('should verify a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(101, [100], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(12, [11], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(12, [10], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(12, [8], options) + }) + }) + + describe('Boolean-Bit Combined Proof Verification (Multi Update, Multi Append)', () => { + it('should verify a Combined Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(1, [0], options) + }) + + it('should verify a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(2, [1], options) + }) + + it('should verify a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(2, [0, 1], options) + }) + + it('should verify a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(128, [2, 4, 12, 15, 127], options) + }) + + it('should verify a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(128, [2, 4, 12, 15, 126, 127], options) + }) + + it('should verify a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(100, [99], options) + }) + + it('should verify a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(100, [2, 4, 12, 15, 99], options) + }) + + it('should verify a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(100, [2, 4, 12, 15, 98, 99], options) + }) + + it('should verify a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(100, [2, 4, 12, 15, 97, 99], options) + }) + + it('should verify a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(100, [2, 4, 12, 15, 97, 98, 99], options) + }) + + it('should verify a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(101, [100], options) + }) + + it('should verify a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(101, [2, 4, 12, 15, 100], options) + }) + + it('should verify a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(101, [2, 4, 12, 15, 99, 100], options) + }) + + it('should verify a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(101, [2, 4, 12, 15, 98, 100], options) + }) + + it('should verify a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(101, [2, 4, 12, 15, 98, 99, 100], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(12, [7, 11], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(12, [7, 10], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(12, [7, 9], options) + }) + + it('should verify a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testCombinedProofVerification(12, [7, 8], options) + }) + + it('should verify a Combined Proof for a sorted-hash 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(1, [0], options) + }) + + it('should verify a Combined Proof for a sorted-hash 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(2, [1], options) + }) + + it('should verify a Combined Proof for a sorted-hash 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(2, [0, 1], options) + }) + + it('should verify a Combined Proof for a sorted-hash 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(128, [2, 4, 12, 15, 127], options) + }) + + it('should verify a Combined Proof for a sorted-hash 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(128, [2, 4, 12, 15, 126, 127], options) + }) + + it('should verify a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(100, [99], options) + }) + + it('should verify a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(100, [2, 4, 12, 15, 99], options) + }) + + it('should verify a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(100, [2, 4, 12, 15, 98, 99], options) + }) + + it('should verify a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(100, [2, 4, 12, 15, 97, 99], options) + }) + + it('should verify a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(100, [2, 4, 12, 15, 97, 98, 99], options) + }) + + it('should verify a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(101, [100], options) + }) + + it('should verify a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(101, [2, 4, 12, 15, 100], options) + }) + + it('should verify a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(101, [2, 4, 12, 15, 99, 100], options) + }) + + it('should verify a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(101, [2, 4, 12, 15, 98, 100], options) + }) + + it('should verify a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(101, [2, 4, 12, 15, 98, 99, 100], options) + }) + + it('should verify a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(12, [7, 11], options) + }) + + it('should verify a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(12, [7, 10], options) + }) + + it('should verify a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(12, [7, 9], options) + }) + + it('should verify a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testCombinedProofVerification(12, [7, 8], options) + }) + }) + }) + + describe('Combined Proof (Updates with Appends)', () => { + describe('Boolean-Array Combined Proofs (Single Update, Single Append)', () => { + describe('Boolean-Array Combined Proof Generation (Single Update, Single Append)', () => { + it('should generate a Combined Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppendProofGeneration(1, 'ff', 0, options) + }) + + it('should generate a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppendProofGeneration(2, 'ff', 1, options) + }) + + it('should generate a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppendProofGeneration(128, 'ff', 127, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppendProofGeneration(100, 'ff', 99, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppendProofGeneration(101, 'ff', 100, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppendProofGeneration(12, 'ff', 11, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppendProofGeneration(12, 'ff', 10, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppendProofGeneration(12, 'ff', 8, options) + }) + }) + + describe('Boolean-Array Combined Proof Execute (Single Update, Single Append)', () => { + it('should use a Combined Proof for a 1-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppend(1, 0, options) + }) + + it('should use a Combined Proof for a 2-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppend(2, 1, options) + }) + + it('should use a Combined Proof for a 128-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppend(128, 127, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppend(100, 99, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppend(101, 100, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppend(12, 11, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppend(12, 10, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUpdateSingleAppend(12, 8, options) + }) + }) + + describe('Boolean-Array Combined Proof Execute Consecutively (Single Update, Single Append)', () => { + it('should use 100 Combined Proofs for a 1-element Merkle Tree, to perform 100 single updates and single appends.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testConsecutiveSingleUpdateSingleAppend(100, 1, options) + }) + }) + }) + + describe.skip('Boolean-Array Combined Proofs (Single Update, Multi Append)', () => { }) + + describe.skip('Boolean-Array Combined Proofs (Multi Update, Single Append)', () => { }) + + describe('Boolean-Array Combined Proofs (Multi Update, Multi Append)', () => { + describe('Boolean-Array Combined Proof Generation (Multi Update, Multi Append)', () => { + it('should generate a Combined Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(1, 'ff', [0], 5, options) + }) + + it('should generate a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(2, 'ff', [1], 5, options) + }) + + it('should generate a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(2, 'ff', [0, 1], 5, options) + }) + + it('should generate a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(128, 'ff', [2, 4, 12, 15, 127], 5, options) + }) + + it('should generate a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(128, 'ff', [2, 4, 12, 15, 126, 127], 5, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [99], 5, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 99], 5, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 98, 99], 5, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 97, 99], 5, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 97, 98, 99], 5, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [100], 5, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 100], 5, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 99, 100], 5, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 98, 100], 5, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 98, 99, 100], 5, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 11], 5, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 10], 5, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 9], 5, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 8], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(1, 'ff', [0], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(2, 'ff', [1], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(2, 'ff', [0, 1], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(128, 'ff', [2, 4, 12, 15, 127], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(128, 'ff', [2, 4, 12, 15, 126, 127], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [99], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 99], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 98, 99], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 97, 99], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 97, 98, 99], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [100], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 100], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 99, 100], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 98, 100], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 98, 99, 100], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 11], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 10], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 9], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 8], 5, options) + }) + }) + + describe('Boolean-Array Combined Proof Execute (Multi Update, Multi Append)', () => { + it('should use a Combined Proof for a 1-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(1, [0], 5, options) + }) + + it('should use a Combined Proof for a 2-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(2, [1], 5, options) + }) + + it('should use a Combined Proof for a 2-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(2, [0, 1], 5, options) + }) + + it('should use a Combined Proof for a 128-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(128, [2, 4, 12, 15, 127], 5, options) + }) + + it('should use a Combined Proof for a 128-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(128, [2, 4, 12, 15, 126, 127], 5, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(100, [99], 5, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 99], 5, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 98, 99], 5, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 97, 99], 5, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 97, 98, 99], 5, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(101, [100], 5, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 100], 5, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 99, 100], 5, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 98, 100], 5, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 98, 99, 100], 5, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(12, [7, 11], 5, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(12, [7, 10], 5, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(12, [7, 9], 5, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUpdateMultiAppend(12, [7, 8], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 1-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(1, [0], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 2-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(2, [1], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 2-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(2, [0, 1], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 128-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(128, [2, 4, 12, 15, 127], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 128-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(128, [2, 4, 12, 15, 126, 127], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(100, [99], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 99], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 98, 99], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 97, 99], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 97, 98, 99], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(101, [100], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 100], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 99, 100], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 98, 100], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 98, 99, 100], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(12, [7, 11], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(12, [7, 10], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(12, [7, 9], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testMultiUpdateMultiAppend(12, [7, 8], 5, options) + }) + }) + + describe('Boolean-Array Combined Proof Execute Consecutively (Multi Update, Multi Append)', () => { + it('should use 100 Combined Proofs for a 1-element Merkle Tree, to perform 100 updates and appends of up to 6 random elements respectively.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testConsecutiveMultiUpdateMultiAppend(100, 1, 6, 6, options) + }) + + it('should use 50 Combined Proofs for a 3-element Merkle Tree, to perform 50 updates and appends of up to 12 random elements respectively.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testConsecutiveMultiUpdateMultiAppend(50, 3, 12, 12, options) + }) + + it('should use 100 Combined Proofs for a sorted-hash 1-element Merkle Tree, to perform 100 updates and appends of up to 6 random elements respectively.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testConsecutiveMultiUpdateMultiAppend(100, 1, 6, 6, options) + }) + + it('should use 50 Combined Proofs for a sorted-hash 3-element Merkle Tree, to perform 50 updates and appends of up to 12 random elements respectively.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: false } + testConsecutiveMultiUpdateMultiAppend(50, 3, 12, 12, options) + }) + }) + }) + + describe('Boolean-Bit Combined Proofs (Single Update, Single Append)', () => { + describe('Boolean-Bit Combined Proof Generation (Single Update, Single Append)', () => { + it('should generate a Combined Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppendProofGeneration(1, 'ff', 0, options) + }) + + it('should generate a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppendProofGeneration(2, 'ff', 1, options) + }) + + it('should generate a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppendProofGeneration(128, 'ff', 127, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppendProofGeneration(100, 'ff', 99, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppendProofGeneration(101, 'ff', 100, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppendProofGeneration(12, 'ff', 11, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppendProofGeneration(12, 'ff', 10, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppendProofGeneration(12, 'ff', 8, options) + }) + }) + + describe('Boolean-Bit Combined Proof Execute (Single Update, Single Append)', () => { + it('should use a Combined Proof for a 1-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppend(1, 0, options) + }) + + it('should use a Combined Proof for a 2-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppend(2, 1, options) + }) + + it('should use a Combined Proof for a 128-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppend(128, 127, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppend(100, 99, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppend(101, 100, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppend(12, 11, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppend(12, 10, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUpdateSingleAppend(12, 8, options) + }) + }) + + describe('Boolean-Bit Combined Proof Execute Consecutively (Single Update, Single Append)', () => { + it('should use 100 Combined Proofs for a 1-element Merkle Tree, to perform 100 single updates and single appends.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testConsecutiveSingleUpdateSingleAppend(100, 1, options) + }) + }) + }) + + describe.skip('Boolean-Bit Combined Proofs (Single Update, Multi Append)', () => { }) + + describe.skip('Boolean-Bit Combined Proofs (Multi Update, Single Append)', () => { }) + + describe('Boolean-Bit (Compact) Combined Proofs (Multi Update, Multi Append)', () => { + describe('Boolean-Bit Combined Proof Generation (Multi Update, Multi Append)', () => { + it('should generate a Combined Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(1, 'ff', [0], 5, options) + }) + + it('should generate a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(2, 'ff', [1], 5, options) + }) + + it('should generate a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(2, 'ff', [0, 1], 5, options) + }) + + it('should generate a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(128, 'ff', [2, 4, 12, 15, 127], 5, options) + }) + + it('should generate a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(128, 'ff', [2, 4, 12, 15, 126, 127], 5, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [99], 5, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 99], 5, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 98, 99], 5, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 97, 99], 5, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 97, 98, 99], 5, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [100], 5, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 100], 5, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 99, 100], 5, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 98, 100], 5, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 98, 99, 100], 5, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 11], 5, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 10], 5, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 9], 5, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 8], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(1, 'ff', [0], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(2, 'ff', [1], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(2, 'ff', [0, 1], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(128, 'ff', [2, 4, 12, 15, 127], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(128, 'ff', [2, 4, 12, 15, 126, 127], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [99], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 99], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 98, 99], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 97, 99], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(100, 'ff', [2, 4, 12, 15, 97, 98, 99], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [100], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 100], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 99, 100], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 98, 100], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(101, 'ff', [2, 4, 12, 15, 98, 99, 100], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 11], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 10], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 9], 5, options) + }) + + it('should generate a Combined Proof for a sorted-hash 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppendProofGeneration(12, 'ff', [7, 8], 5, options) + }) + }) + + describe('Boolean-Bit Combined Proof Execute (Multi Update, Multi Append)', () => { + it('should use a Combined Proof for a 1-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(1, [0], 5, options) + }) + + it('should use a Combined Proof for a 2-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(2, [1], 5, options) + }) + + it('should use a Combined Proof for a 2-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(2, [0, 1], 5, options) + }) + + it('should use a Combined Proof for a 128-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(128, [2, 4, 12, 15, 127], 5, options) + }) + + it('should use a Combined Proof for a 128-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(128, [2, 4, 12, 15, 126, 127], 5, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(100, [99], 5, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 99], 5, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 98, 99], 5, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 97, 99], 5, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 97, 98, 99], 5, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(101, [100], 5, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 100], 5, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 99, 100], 5, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 98, 100], 5, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 98, 99, 100], 5, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(12, [7, 11], 5, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(12, [7, 10], 5, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(12, [7, 9], 5, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUpdateMultiAppend(12, [7, 8], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 1-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(1, [0], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 2-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(2, [1], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 2-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(2, [0, 1], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 128-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(128, [2, 4, 12, 15, 127], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 128-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(128, [2, 4, 12, 15, 126, 127], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(100, [99], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 99], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 98, 99], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 97, 99], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 100-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(100, [2, 4, 12, 15, 97, 98, 99], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(101, [100], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 100], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 99, 100], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 98, 100], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 101-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(101, [2, 4, 12, 15, 98, 99, 100], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(12, [7, 11], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(12, [7, 10], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(12, [7, 9], 5, options) + }) + + it('should use a Combined Proof for a sorted-hash 12-element Merkle Tree, to update and append elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testMultiUpdateMultiAppend(12, [7, 8], 5, options) + }) + }) + + describe('Boolean-Bit Combined Proof Execute Consecutively (Multi Update, Multi Append)', () => { + it('should use 100 Combined Proofs for a 1-element Merkle Tree, to perform 100 updates and appends of up to 6 random elements respectively.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testConsecutiveMultiUpdateMultiAppend(100, 1, 6, 6, options) + }) + + it('should use 50 Combined Proofs for a 3-element Merkle Tree, to perform 50 updates and appends of up to 12 random elements respectively.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testConsecutiveMultiUpdateMultiAppend(50, 3, 12, 12, options) + }) + + it('should use 100 Combined Proofs for a sorted-hash 1-element Merkle Tree, to perform 100 updates and appends of up to 6 random elements respectively.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testConsecutiveMultiUpdateMultiAppend(100, 1, 6, 6, options) + }) + + it('should use 50 Combined Proofs for a sorted-hash 3-element Merkle Tree, to perform 50 updates and appends of up to 12 random elements respectively.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: false, compact: true } + testConsecutiveMultiUpdateMultiAppend(50, 3, 12, 12, options) + }) + }) + }) + }) + + describe('Combined Proof (Uses with Appends)', () => { + describe('Boolean-Array Combined Proofs (Single Use, Single Append)', () => { + describe('Boolean-Array Combined Proof Generation (Single Use, Single Append)', () => { + it('should generate a Combined Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppendProofGeneration(1, 'ff', 0, options) + }) + + it('should generate a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppendProofGeneration(2, 'ff', 1, options) + }) + + it('should generate a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppendProofGeneration(128, 'ff', 127, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppendProofGeneration(100, 'ff', 99, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppendProofGeneration(101, 'ff', 100, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppendProofGeneration(12, 'ff', 11, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppendProofGeneration(12, 'ff', 10, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppendProofGeneration(12, 'ff', 8, options) + }) + }) + + describe('Boolean-Array Combined Proof Execute (Single Use, Single Append)', () => { + it('should use a Combined Proof for a 1-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppend(1, 0, options) + }) + + it('should use a Combined Proof for a 2-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppend(2, 1, options) + }) + + it('should use a Combined Proof for a 128-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppend(128, 127, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppend(100, 99, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppend(101, 100, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppend(12, 11, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppend(12, 10, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testSingleUseSingleAppend(12, 8, options) + }) + }) + + describe('Boolean-Array Combined Proof Execute Consecutively (Single Use, Single Append)', () => { + it('should use 100 Combined Proofs for a 1-element Merkle Tree, to perform 100 single uses and single appends.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testConsecutiveSingleUseSingleAppend(100, 1, options) + }) + }) + }) + + describe.skip('Boolean-Array Combined Proofs (Single Use, Multi Append)', () => { }) + + describe.skip('Boolean-Array Combined Proofs (Multi Use, Single Append)', () => { }) + + describe('Boolean-Array Combined Proofs (Multi Use, Multi Append)', () => { + describe('Boolean-Array Combined Proof Generation (Multi Use, Multi Append)', () => { + it('should generate a Combined Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUseMultiAppendProofGeneration(1, 'ff', [0], 5, options) + }) + + it('should generate a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUseMultiAppendProofGeneration(2, 'ff', [1], 5, options) + }) + + it('should generate a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUseMultiAppendProofGeneration(128, 'ff', [2, 4, 12, 15, 127], 5, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUseMultiAppendProofGeneration(100, 'ff', [99], 5, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUseMultiAppendProofGeneration(101, 'ff', [100], 5, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUseMultiAppendProofGeneration(12, 'ff', [7, 11], 5, options) + }) + }) + + describe('Boolean-Array Combined Proof Execute (Multi Use, Multi Append)', () => { + it('should use a Combined Proof for a 1-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUseMultiAppend(1, [0], 5, options) + }) + + it('should use a Combined Proof for a 2-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUseMultiAppend(2, [1], 5, options) + }) + + it('should use a Combined Proof for a 128-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUseMultiAppend(128, [2, 4, 12, 15, 127], 5, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUseMultiAppend(100, [99], 5, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUseMultiAppend(101, [100], 5, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testMultiUseMultiAppend(12, [7, 11], 5, options) + }) + }) + + describe('Boolean-Array Combined Proof Execute Consecutively (Multi Use, Multi Append)', () => { + it('should use 100 Combined Proofs for a 1-element Merkle Tree, to perform 100 uses and appends of up to 6 random elements respectively.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testConsecutiveMultiUseMultiAppend(100, 1, 6, 6, options) + }) + }) + }) + + describe('Boolean-Bit Combined Proofs (Single Use, Single Append)', () => { + describe('Boolean-Bit Combined Proof Generation (Single Use, Single Append)', () => { + it('should generate a Combined Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppendProofGeneration(1, 'ff', 0, options) + }) + + it('should generate a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppendProofGeneration(2, 'ff', 1, options) + }) + + it('should generate a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppendProofGeneration(128, 'ff', 127, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppendProofGeneration(100, 'ff', 99, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppendProofGeneration(101, 'ff', 100, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppendProofGeneration(12, 'ff', 11, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppendProofGeneration(12, 'ff', 10, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppendProofGeneration(12, 'ff', 8, options) + }) + }) + + describe('Boolean-Bit Combined Proof Execute (Single Use, Single Append)', () => { + it('should use a Combined Proof for a 1-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppend(1, 0, options) + }) + + it('should use a Combined Proof for a 2-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppend(2, 1, options) + }) + + it('should use a Combined Proof for a 128-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppend(128, 127, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppend(100, 99, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppend(101, 100, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppend(12, 11, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppend(12, 10, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to use 1 and append 1 element.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testSingleUseSingleAppend(12, 8, options) + }) + }) + + describe('Boolean-Bit Combined Proof Execute Consecutively (Single Use, Single Append)', () => { + it('should use 100 Combined Proofs for a 1-element Merkle Tree, to perform 100 single uses and single appends.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testConsecutiveSingleUseSingleAppend(100, 1, options) + }) + }) + }) + + describe.skip('Boolean-Bit Combined Proofs (Single Use, Multi Append)', () => { }) + + describe.skip('Boolean-Bit Combined Proofs (Multi Use, Single Append)', () => { }) + + describe('Boolean-Bit (Compact) Combined Proofs (Multi Use, Multi Append)', () => { + describe('Boolean-Bit Combined Proof Generation (Multi Use, Multi Append)', () => { + it('should generate a Combined Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUseMultiAppendProofGeneration(1, 'ff', [0], 5, options) + }) + + it('should generate a Combined Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUseMultiAppendProofGeneration(2, 'ff', [1], 5, options) + }) + + it('should generate a Combined Proof for a 128-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUseMultiAppendProofGeneration(128, 'ff', [2, 4, 12, 15, 127], 5, options) + }) + + it('should generate a Combined Proof for a 100-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUseMultiAppendProofGeneration(100, 'ff', [99], 5, options) + }) + + it('should generate a Combined Proof for a 101-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUseMultiAppendProofGeneration(101, 'ff', [100], 5, options) + }) + + it('should generate a Combined Proof for a 12-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUseMultiAppendProofGeneration(12, 'ff', [7, 11], 5, options) + }) + }) + + describe('Boolean-Bit Combined Proof Execute (Multi Use, Multi Append)', () => { + it('should use a Combined Proof for a 1-element Merkle Tree, to use and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUseMultiAppend(1, [0], 5, options) + }) + + it('should use a Combined Proof for a 2-element Merkle Tree, to use and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUseMultiAppend(2, [1], 5, options) + }) + + it('should use a Combined Proof for a 128-element Merkle Tree, to use and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUseMultiAppend(128, [2, 4, 12, 15, 127], 5, options) + }) + + it('should use a Combined Proof for a 100-element Merkle Tree, to use and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUseMultiAppend(100, [99], 5, options) + }) + + it('should use a Combined Proof for a 101-element Merkle Tree, to use and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUseMultiAppend(101, [100], 5, options) + }) + + it('should use a Combined Proof for a 12-element Merkle Tree, to use and append elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testMultiUseMultiAppend(12, [7, 11], 5, options) + }) + }) + + describe('Boolean-Bit Combined Proof Execute Consecutively (Multi Use, Multi Append)', () => { + it('should use 100 Combined Proofs for a 1-element Merkle Tree, to perform 100 uses and appends of up to 6 random elements respectively.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testConsecutiveMultiUseMultiAppend(100, 1, 6, 6, options) + }) + }) + }) + }) + + describe('Size Proofs', () => { + describe('Size Proof Generation', () => { + it('should generate a Size Proof for a 0-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + + const expected = { + decommitments: [], + } + + testSizeProofGeneration(0, 'ff', expected, options) + }) + + it('should generate a Size Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + + const expected = { + decommitments: ['0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60'].map(Buffer.from), + } + + testSizeProofGeneration(1, 'ff', expected, options) + }) + + it('should generate a Size Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + + const expected = { + decommitments: ['a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27'].map(Buffer.from), + } + + testSizeProofGeneration(2, 'ff', expected, options) + }) + + it('should generate a Size Proof for a 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + + const expected = { + decommitments: [ + 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27', + 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b', + ].map(Buffer.from), + } + + testSizeProofGeneration(3, 'ff', expected, options) + }) + + it('should generate a Size Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + + const expected = { + decommitments: ['0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4'].map(Buffer.from), + } + + testSizeProofGeneration(8, 'ff', expected, options) + }) + + it('should generate a Size Proof for a 15-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + + const expected = { + decommitments: [ + '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4', + 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b', + '712ed55abe1946b941876a6230b3135edadc400a18897e029ffdbff6900503e6', + 'e481ff292c1b323f27dd2e7b0da511947e0d349a0616a739ea628a3a5888c529', + ].map(Buffer.from), + } + + testSizeProofGeneration(15, 'ff', expected, options) + }) + + it('should generate a Size Proof for a 20-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + + const expected = { + decommitments: [ + 'c7ec3e428ae2869b12c1b8e12a84e56f0d7f3cbe752cd1c3775158cf846412be', + 'febc2d558e22b7e32db3a5dd0b4d8ac3dac5835493955c53e3eb0f8fdb2f4954', + ].map(Buffer.from), + } + + testSizeProofGeneration(20, 'ff', expected, options) + }) + + it('should generate a compact Size Proof for a 0-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true, simple: false } + + const expected = { + compactProof: [], + } + + testSizeProofGeneration(0, 'ff', expected, options) + }) + + it('should generate a compact Size Proof for a 20-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true, simple: false } + + const expected = { + compactProof: [ + 'c7ec3e428ae2869b12c1b8e12a84e56f0d7f3cbe752cd1c3775158cf846412be', + 'febc2d558e22b7e32db3a5dd0b4d8ac3dac5835493955c53e3eb0f8fdb2f4954', + ].map(Buffer.from), + } + + testSizeProofGeneration(20, 'ff', expected, options) + }) + + it('should generate a simple Size Proof for a 0-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: true } + + const expected = { + element: Buffer.from('0000000000000000000000000000000000000000000000000000000000000000'), + } + + testSizeProofGeneration(0, 'ff', expected, options) + }) + + it('should generate a simple Size Proof for a 20-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: true } + + const expected = { + element: Buffer.from('56afa93d33699c72a6989f3b114b1b474d3ec202164549fe3d616cca61fb071b'), + } + + testSizeProofGeneration(20, 'ff', expected, options) + }) + + it('should generate a simple Size Proof for a 20-element sorted-hash Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false, simple: true } + + const expected = { + element: Buffer.from('5944736c577a3a171994fca82f54afa1cd102dd1a0b3c3bd9e71c82038a2172e'), + } + + testSizeProofGeneration(20, 'ff', expected, options) + }) + }) + + describe('Size Proof Verification', () => { + it('should verify a Size Proof for a 0-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + testSizeProofVerification(0, options) + }) + + it('should verify a Size Proof for a 1-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + testSizeProofVerification(1, options) + }) + + it('should verify a Size Proof for a 2-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + testSizeProofVerification(2, options) + }) + + it('should verify a Size Proof for a 3-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + testSizeProofVerification(3, options) + }) + + it('should verify a Size Proof for a 8-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + testSizeProofVerification(8, options) + }) + + it('should verify a Size Proof for a 15-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + testSizeProofVerification(15, options) + }) + + it('should verify a Size Proof for a 20-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: false } + testSizeProofVerification(20, options) + }) + + it('should verify a compact Size Proof for a 0-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true, simple: false } + testSizeProofVerification(0, options) + }) + + it('should verify a compact Size Proof for a 20-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true, simple: false } + testSizeProofVerification(20, options) + }) + + it('should verify a simple Size Proof for a 0-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: true } + testSizeProofVerification(0, options) + }) + + it('should verify a simple Size Proof for a 20-element Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false, simple: true } + testSizeProofVerification(20, options) + }) + + it('should verify a simple Size Proof for a 20-element sorted-hash Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false, simple: true } + testSizeProofVerification(20, options) + }) + }) + }) + + describe('Arbitrary Element Sizes', () => { + describe('Merkle Tree Construction', () => { + it('should build a 28-element Merkle Tree of 10-byte elements.', () => { + const expected = { + root: 'f22ea9dce286a16f532002cfee669d6755131095e310e2414460cf05173bf970', + elementRoot: 'c6e8dab5e6d43912a996048145f94af81fc5314c61e74ed93eb2143189fe13ba', + depth: 5, + } + + testBuildTree(28, 'ff', expected, { unbalanced: true, sortedHash: false }, { size: 10 }) + }) + + it('should build a 28-element Merkle Tree of 100-byte elements.', () => { + const expected = { + root: 'd341738448ea99be1cbb5d9709dc49e9c55e7ca903971fa4f57650aa7c55067a', + elementRoot: '55b732cbfaedd53e9244ed2dc1059c8e0534ce03cbb71effec5fd666ad2c5835', + depth: 5, + } + + testBuildTree(28, 'ff', expected, { unbalanced: true, sortedHash: false }, { size: 100 }) + }) + + it('should build a 28-element Merkle Tree of random size elements.', () => { + const expected = { + root: 'f3d9d28abdf4024da0b75f98cf0cf3dbb5de429e4cddb69e8affebff325d53d0', + elementRoot: '9675e208b3b3207d4e76237ce5606d221f430b85ba193cec69fe84888a07cecc', + depth: 5, + } + + testBuildTree(28, 'ff', expected, { unbalanced: true, sortedHash: false }, { size: 0 }) + }) + }) + + describe('Single Proof Update Consecutive Uses', () => { + it('should use 10 Compact Single Proofs for a 25-element, of random size, Merkle Tree, to update an 10 elements consecutively.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testConsecutiveSingleUpdate(10, 25, options, { size: 0 }) + }) + }) + + describe('Index and Existence Multi Proof Update Consecutive Uses', () => { + it('should use 10 Compact Multi Proofs for a 19-element, of random size, Merkle Tree, to perform 10 updates of up to 6 random elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: true } + testConsecutiveMultiUpdate(10, 19, 6, options, { size: 0 }) + }) + }) + + describe('Existence-Only Boolean-Array Multi Proof Update Consecutive Uses', () => { + it('should use 10 Multi Proofs for a 19-element, of random size, Merkle Tree, to perform 10 updates of up to 6 random elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + testConsecutiveMultiUpdate(10, 19, 6, options, { size: 0 }) + }) + }) + + describe('Existence-Only Boolean-Bit Multi Proof Update Consecutive Uses', () => { + it('should use 10 Multi Proofs for a 19-element, of random size, Merkle Tree, to perform 10 updates of up to 6 random elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testConsecutiveMultiUpdate(10, 19, 6, options, { size: 0 }) + }) + }) + + describe('Append Proof Append Consecutive Uses', () => { + it('should use 10 Compact Single Append Proofs for a 160-element, of random size, Merkle Tree, to append an 10 elements consecutively.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testConsecutiveSingleAppend(10, 160, options, { size: 0 }) + }) + + it('should use 10 Multi Append Proofs for a 0-element Merkle Tree, to perform 10 appends of up to 4 random elements, of random size.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testConsecutiveMultiAppend(10, 0, 4, options, { size: 0 }) + }) + }) + }) +}) diff --git a/js/src/ts/tests/parial-merkle.test.ts b/js/src/ts/tests/parial-merkle.test.ts new file mode 100644 index 0000000..ab38031 --- /dev/null +++ b/js/src/ts/tests/parial-merkle.test.ts @@ -0,0 +1,2431 @@ +import { testTreeFromSingleProof, testTreeFromSingleUpdateProof, testTreeFromMultiProof, testTreeFromMultiUpdateProof, testTreeFromSingleAppendProof, testTreeFromMultiAppendProof, testGenerateSingleProofFromPartial, testGenerateSingleUpdateProofFromSinglePartial, testGenerateMultiUpdateProofFromMultiPartial, testCheckElements, testSetElement, testSetElements, testAppendElement, testAppendElements } from "./helpers/partial-tree-helpers" + +describe('Partial Merkle Trees', () => { + describe('Build Partial Trees From Single Proofs', () => { + describe('Balanced', () => { + it('should build an 8-element Partial Tree from a Single Proof.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + + const elements = Array(8).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + + const tree = Array(16).fill(null) + tree[10] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[11] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[4] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[2] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[3] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[1] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[0] = 'd2fa9d47845f1571f1318afaaabc63a55cc57af3f511e42fc30e05f171d6853d' + + const expected = { elements, tree } + + testTreeFromSingleProof(8, 'ff', 2, expected, options) + }) + + it('should build a 1-element Partial Tree from a Single Proof.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + + const elements = Array(1).fill(null) + elements[0] = '06d41322d79dfed27126569cb9a80eb0967335bf2f3316359d2a93c779fcd38a' + + const tree = Array(2).fill(null) + tree[1] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[0] = 'c83b51dc238800a2852366108ab7df1e32b35e99905c5d845ff5a652f0fb58a8' + + const expected = { elements, tree } + + testTreeFromSingleProof(1, 'ff', 0, expected, options) + }) + + it('should build a sorted-hash 8-element Partial Tree from a Single Proof.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + + const elements = Array(8).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + + const tree = Array(16).fill(null) + tree[10] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[11] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[4] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[2] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[3] = 'bc481a454a66b25fd1adf8b6b88cbcac3783d39d5ab1e4c45d114846da10274c' + tree[1] = '7f8dc34b7b4e06eff546283358ff8d7a988b62bc266f6337f8234c9a84778221' + tree[0] = '6764fd6d226590b844285c3d0f1e12bbd19cb7d1ee8277b0fb5b9b45efbbffb6' + + const expected = { elements, tree } + + testTreeFromSingleProof(8, 'ff', 2, expected, options) + }) + + it('should build a sorted-hash 1-element Partial Tree from a Single Proof.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + + const elements = Array(1).fill(null) + elements[0] = '06d41322d79dfed27126569cb9a80eb0967335bf2f3316359d2a93c779fcd38a' + + const tree = Array(2).fill(null) + tree[1] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[0] = 'c83b51dc238800a2852366108ab7df1e32b35e99905c5d845ff5a652f0fb58a8' + + const expected = { elements, tree } + + testTreeFromSingleProof(1, 'ff', 0, expected, options) + }) + + it('should build an 8-element Partial Tree from a compact Single Proof.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + + const elements = Array(8).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + + const tree = Array(16).fill(null) + tree[10] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[11] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[4] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[2] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[3] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[1] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[0] = 'd2fa9d47845f1571f1318afaaabc63a55cc57af3f511e42fc30e05f171d6853d' + + const expected = { elements, tree } + + testTreeFromSingleProof(8, 'ff', 2, expected, options) + }) + + it('should build a 1-element Partial Tree from a compact Single Proof.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + + const elements = Array(1).fill(null) + elements[0] = '06d41322d79dfed27126569cb9a80eb0967335bf2f3316359d2a93c779fcd38a' + + const tree = Array(2).fill(null) + tree[1] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[0] = 'c83b51dc238800a2852366108ab7df1e32b35e99905c5d845ff5a652f0fb58a8' + + const expected = { elements, tree } + + testTreeFromSingleProof(1, 'ff', 0, expected, options) + }) + }) + + describe('Unbalanced', () => { + it('should build a 9-element Partial Tree from a Single Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(9).fill(null) + elements[8] = '137b5194eb17aa8cc50e40d7054133657e5e5e13a6a746694cc6d464993ea3f1' + + const tree = Array(32).fill(null) + tree[24] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[12] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[6] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[3] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[2] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[1] = '5449a839359e08115bbc14ed1795892a3a8562d583744e1a1fa146d273ff1f55' + tree[0] = '743605bc7fcb07d66ecf3f2b5fcea24bfb27901bfbdb7baf6a194aa45d62461d' + + const expected = { elements, tree } + + testTreeFromSingleProof(9, 'ff', 8, expected, options) + }) + + it('should build a 27-element Partial Tree from a Single Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(27).fill(null) + elements[25] = '07f7dfa8d4ab639269639378be0af23e0a8edb7dd76f1a6b84d9011a930c1036' + + const tree = Array(64).fill(null) + tree[57] = 'f26d6a46e3dd889bd514b8b08b003efa113e9e1886681ad5d518fa292035a392' + tree[56] = 'c43e6f0c51c26c040dc4a40e372839ccc4a53c1762504da451819ae9e93d239a' + tree[28] = 'dd1c66ff9c67d05f1aeb7ec6196210db830b83ac973345b2908d31677d52c311' + tree[29] = '289b3d65643b54739112fa7df258736b511ed1b5611e4f9ce681ae39fbd5fd8b' + tree[14] = '24975f4bf474a6484760e2489b06f74e0f107c06507c1c3daa2d264a3eaca401' + tree[7] = '24975f4bf474a6484760e2489b06f74e0f107c06507c1c3daa2d264a3eaca401' + tree[6] = '88d2a11c3b0935fc6a30e3b0a69fa58a84de08ea333248f23e5d747613fc04f9' + tree[3] = 'fa0c9c1524f303d1d23d569be579c73757c184b063d106da9f87589048bd81e8' + tree[2] = 'c7ec3e428ae2869b12c1b8e12a84e56f0d7f3cbe752cd1c3775158cf846412be' + tree[1] = '864ec5c4fc04d31a78f34609510aae2359aadd8f6552c90f4b19f577320c95a3' + tree[0] = '82d4289279a9a69edeffe02b207e8b4c71f924361c5db2f08ec2f40d97ceb4cf' + + const expected = { elements, tree } + + testTreeFromSingleProof(27, 'ff', 25, expected, options) + }) + + it('should build a 100-element Partial Tree from a Single Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(100).fill(null) + elements[97] = 'b646b7374351011433374985a8369211f174e82217b148284dd289aea0924048' + + const tree = Array(256).fill(null) + tree[225] = 'df772d27e4b7660ec33c84fad6f1cefecc233191a1a6c96392ebbaadb1c1a745' + tree[224] = '4ac05d0ec2e247aad1065f712d3d6934938e4709f224a0466f558bdf2e4cbf9c' + tree[112] = 'd902920fde7efe9ec00bd50e4195851294dcb7178a0aa8c5ebc913d5659586f9' + tree[113] = 'bbc26fa1ff8c9f841d4f4758cccac1def0f9929c30c949451d4e71e4ded0a681' + tree[56] = 'fc73842409eddc3eaa4152b720a42c61c2174fda448d9e5282d6b9760fb1d823' + tree[28] = 'fc73842409eddc3eaa4152b720a42c61c2174fda448d9e5282d6b9760fb1d823' + tree[14] = 'fc73842409eddc3eaa4152b720a42c61c2174fda448d9e5282d6b9760fb1d823' + tree[7] = 'fc73842409eddc3eaa4152b720a42c61c2174fda448d9e5282d6b9760fb1d823' + tree[6] = '06f8f83483a72750b8ba34cbe8fd54cc1243479b12f7b659075311dc54800203' + tree[3] = 'c7ac26658765aeb6a62d2ff9670a675e6a701dc072d31882537c17797540d1ba' + tree[2] = 'eb98df4415ff9a93976bb26b84f3819662fe31939e022cfa52d9061de351f6d5' + tree[1] = 'e6d48938549ee639bbd7650edd189b1a6d3c375b9392dfa4d53168696aac096f' + tree[0] = '1971f4a916665d02ab059da67d0212746fc7e0d7bf953e53f6426e51cf3f3eb8' + + const expected = { elements, tree } + + testTreeFromSingleProof(100, 'ff', 97, expected, options) + }) + + it('should build a sorted-hash 9-element Partial Tree from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + + const elements = Array(9).fill(null) + elements[8] = '137b5194eb17aa8cc50e40d7054133657e5e5e13a6a746694cc6d464993ea3f1' + + const tree = Array(32).fill(null) + tree[24] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[12] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[6] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[3] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[2] = '7f8dc34b7b4e06eff546283358ff8d7a988b62bc266f6337f8234c9a84778221' + tree[1] = '86620d93d22f2d06344f81166356ed881cfdc36c8b35a7115e8b0daad4d56ee4' + tree[0] = '4c10104ea544f26190809c1117a092b18c8d7ab892f23c30a0f0cdb2c5242c48' + + const expected = { elements, tree } + + testTreeFromSingleProof(9, 'ff', 8, expected, options) + }) + + it('should build a sorted-hash 27-element Partial Tree from a Single Proof', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + + const elements = Array(27).fill(null) + elements[25] = '07f7dfa8d4ab639269639378be0af23e0a8edb7dd76f1a6b84d9011a930c1036' + + const tree = Array(64).fill(null) + tree[57] = 'f26d6a46e3dd889bd514b8b08b003efa113e9e1886681ad5d518fa292035a392' + tree[56] = 'c43e6f0c51c26c040dc4a40e372839ccc4a53c1762504da451819ae9e93d239a' + tree[28] = 'dd1c66ff9c67d05f1aeb7ec6196210db830b83ac973345b2908d31677d52c311' + tree[29] = '289b3d65643b54739112fa7df258736b511ed1b5611e4f9ce681ae39fbd5fd8b' + tree[14] = 'd7a7143b496e3b66ffe1b218adb9437298aaa02bb63526f6f0c0ea1e30c1bdfb' + tree[7] = 'd7a7143b496e3b66ffe1b218adb9437298aaa02bb63526f6f0c0ea1e30c1bdfb' + tree[6] = 'c62e1d7cf122111fa068da94e48ecd21cb02bba4bd41d56e9f4b69a4509a2962' + tree[3] = '2152896c688a914f712d6ec00dfdfb64f04f2a59ff31f6fb6f2b4ee49137ea90' + tree[2] = '2c2cdc952c9d537709959cd357f6268fff33e5e21147f1a23db6cae78fb91eb9' + tree[1] = '631a369d35cbaa7521202f8746fbb2727209acb39c71437ab607efc0356e57c5' + tree[0] = 'f486e0285ce20258ec9bc865b05ca7b781c80356a80c4589cebfe6a21920246c' + + const expected = { elements, tree } + + testTreeFromSingleProof(27, 'ff', 25, expected, options) + }) + + it('should build a sorted-hash 100-element Partial Tree from a Single Proof', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + + const elements = Array(100).fill(null) + elements[97] = 'b646b7374351011433374985a8369211f174e82217b148284dd289aea0924048' + + const tree = Array(256).fill(null) + tree[225] = 'df772d27e4b7660ec33c84fad6f1cefecc233191a1a6c96392ebbaadb1c1a745' + tree[224] = '4ac05d0ec2e247aad1065f712d3d6934938e4709f224a0466f558bdf2e4cbf9c' + tree[112] = 'd902920fde7efe9ec00bd50e4195851294dcb7178a0aa8c5ebc913d5659586f9' + tree[113] = 'bbc26fa1ff8c9f841d4f4758cccac1def0f9929c30c949451d4e71e4ded0a681' + tree[56] = 'fd40325cdd68255b4c4d9c1ef4b7f96d3e9fb685ed1fe4a66a8b07a5b58c4d11' + tree[28] = 'fd40325cdd68255b4c4d9c1ef4b7f96d3e9fb685ed1fe4a66a8b07a5b58c4d11' + tree[14] = 'fd40325cdd68255b4c4d9c1ef4b7f96d3e9fb685ed1fe4a66a8b07a5b58c4d11' + tree[7] = 'fd40325cdd68255b4c4d9c1ef4b7f96d3e9fb685ed1fe4a66a8b07a5b58c4d11' + tree[6] = '904afce76e0f7ccead463e22aec76018c1450afd3deb4f387e0617ef39721685' + tree[3] = 'eff316f1875dfc72ac589474bbe0902e326b41894a7d91bbcdcb496d4c7b91af' + tree[2] = 'bb9a6e5787ae741c6a0e75a360aefe75ee06284ece1edddc1573ac9462945e7f' + tree[1] = '5c0bd9cce6a6b4da7a61e98b6d1f14fa323a5e7d583798aab4f17617b40f62f0' + tree[0] = '2b77cea5d8a8c7da68b24f3cefbfe4c7ec98d4f039b91dcdd841fb5c4f739cfd' + + const expected = { elements, tree } + + testTreeFromSingleProof(100, 'ff', 97, expected, options) + }) + + it('should build a 100-element Partial Tree from a compact Single Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + + const elements = Array(100).fill(null) + elements[97] = 'b646b7374351011433374985a8369211f174e82217b148284dd289aea0924048' + + const tree = Array(256).fill(null) + tree[225] = 'df772d27e4b7660ec33c84fad6f1cefecc233191a1a6c96392ebbaadb1c1a745' + tree[224] = '4ac05d0ec2e247aad1065f712d3d6934938e4709f224a0466f558bdf2e4cbf9c' + tree[112] = 'd902920fde7efe9ec00bd50e4195851294dcb7178a0aa8c5ebc913d5659586f9' + tree[113] = 'bbc26fa1ff8c9f841d4f4758cccac1def0f9929c30c949451d4e71e4ded0a681' + tree[56] = 'fc73842409eddc3eaa4152b720a42c61c2174fda448d9e5282d6b9760fb1d823' + tree[28] = 'fc73842409eddc3eaa4152b720a42c61c2174fda448d9e5282d6b9760fb1d823' + tree[14] = 'fc73842409eddc3eaa4152b720a42c61c2174fda448d9e5282d6b9760fb1d823' + tree[7] = 'fc73842409eddc3eaa4152b720a42c61c2174fda448d9e5282d6b9760fb1d823' + tree[6] = '06f8f83483a72750b8ba34cbe8fd54cc1243479b12f7b659075311dc54800203' + tree[3] = 'c7ac26658765aeb6a62d2ff9670a675e6a701dc072d31882537c17797540d1ba' + tree[2] = 'eb98df4415ff9a93976bb26b84f3819662fe31939e022cfa52d9061de351f6d5' + tree[1] = 'e6d48938549ee639bbd7650edd189b1a6d3c375b9392dfa4d53168696aac096f' + tree[0] = '1971f4a916665d02ab059da67d0212746fc7e0d7bf953e53f6426e51cf3f3eb8' + + const expected = { elements, tree } + + testTreeFromSingleProof(100, 'ff', 97, expected, options) + }) + }) + }) + + describe('Build Partial Trees From Single Update Proofs', () => { + describe('Balanced', () => { + it('should build an 8-element Partial Tree from a Single Update Proof.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + + const elements = Array(8).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(16).fill(null) + tree[10] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[11] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[5] = 'ec3bf1549ba1a76a9647403c4fbfa861de4a9bd594d3591f93a25597b5d4a373' + tree[4] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[2] = '209b3596df36a23f39a11d2292acdf5075a68644f49393b3739e7b8ce99acb18' + tree[3] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[1] = '1078ae4bb62347da6c2e6445a352f4ac8399535dcd913416a88edc1767f14b16' + tree[0] = '5f9e3e380d71f3828a04f1d44bdd74e441938c088bababc4e13a528f48a65230' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(8, 'ff', 2, expected, options) + }) + + it('should build a 1-element Partial Tree from a Single Update Proof.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + + const elements = Array(1).fill(null) + elements[0] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(2).fill(null) + tree[1] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[0] = '2524b2088139a21ce5deaed4424127ad4efbd4859298df2a280d467154d4f898' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(1, 'ff', 0, expected, options) + }) + + it('should build a sorted-hash 8-element Partial Tree from a Single Update Proof.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + + const elements = Array(8).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(16).fill(null) + tree[10] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[11] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[5] = 'ec3bf1549ba1a76a9647403c4fbfa861de4a9bd594d3591f93a25597b5d4a373' + tree[4] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[2] = '209b3596df36a23f39a11d2292acdf5075a68644f49393b3739e7b8ce99acb18' + tree[3] = 'bc481a454a66b25fd1adf8b6b88cbcac3783d39d5ab1e4c45d114846da10274c' + tree[1] = '50b40f707599689bc8023eb6de04e9f00532e4de06077f4f40d99a6fea08cd7f' + tree[0] = '69c1b153b07cf3524f28678a2deffba09085aed0296da9a583b4307bcb4b7157' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(8, 'ff', 2, expected, options) + }) + + it('should build a sorted-hash 1-element Partial Tree from a Single Update Proof.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + + const elements = Array(1).fill(null) + elements[0] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(2).fill(null) + tree[1] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[0] = '2524b2088139a21ce5deaed4424127ad4efbd4859298df2a280d467154d4f898' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(1, 'ff', 0, expected, options) + }) + + it('should build an 8-element Partial Tree from a compact Single Update Proof.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + + const elements = Array(8).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(16).fill(null) + tree[10] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[11] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[5] = 'ec3bf1549ba1a76a9647403c4fbfa861de4a9bd594d3591f93a25597b5d4a373' + tree[4] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[2] = '209b3596df36a23f39a11d2292acdf5075a68644f49393b3739e7b8ce99acb18' + tree[3] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[1] = '1078ae4bb62347da6c2e6445a352f4ac8399535dcd913416a88edc1767f14b16' + tree[0] = '5f9e3e380d71f3828a04f1d44bdd74e441938c088bababc4e13a528f48a65230' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(8, 'ff', 2, expected, options) + }) + + it('should build a 1-element Partial Tree from a compact Single Update Proof.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + + const elements = Array(1).fill(null) + elements[0] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(2).fill(null) + tree[1] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[0] = '2524b2088139a21ce5deaed4424127ad4efbd4859298df2a280d467154d4f898' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(1, 'ff', 0, expected, options) + }) + }) + + describe('Unbalanced', () => { + it('should build a 9-element Partial Tree from a Single Update Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(9).fill(null) + elements[8] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(32).fill(null) + tree[24] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[12] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[6] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[3] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[2] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[1] = 'da3e7fc24e77582fb320b79b59bdb74edb3c4ee5d59f1fe0a38c21af7294485a' + tree[0] = 'dc24680d74fb46576df0172fff0f778becd7c3053e8b8fbf142e15caf2399125' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(9, 'ff', 8, expected, options) + }) + + it('should build a 27-element Partial Tree from a Single Update Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(27).fill(null) + elements[25] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(64).fill(null) + tree[57] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[56] = 'c43e6f0c51c26c040dc4a40e372839ccc4a53c1762504da451819ae9e93d239a' + tree[28] = 'fed21bdfb042f5e53cae1173e43a6ccedd53a91d25995912a48ab02584135568' + tree[29] = '289b3d65643b54739112fa7df258736b511ed1b5611e4f9ce681ae39fbd5fd8b' + tree[14] = 'd5519a13a967dff94ca0ddb572261749ad5b1506a1e31236975bb5c9cb607899' + tree[7] = 'd5519a13a967dff94ca0ddb572261749ad5b1506a1e31236975bb5c9cb607899' + tree[6] = '88d2a11c3b0935fc6a30e3b0a69fa58a84de08ea333248f23e5d747613fc04f9' + tree[3] = '636ac5d86984c900efe32640943d4dc25ea08b6b8702a44d696acc3f9ac8bf06' + tree[2] = 'c7ec3e428ae2869b12c1b8e12a84e56f0d7f3cbe752cd1c3775158cf846412be' + tree[1] = '4bb6bf484b7d59b9e2bf077dc2371b4d789b09f81642f234c83a4c5531f771d6' + tree[0] = '86736648d4ff694b44aa7a9d1a5b340a553911a44629d7c8adbeac7532ea3821' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(27, 'ff', 25, expected, options) + }) + + it('should build a 100-element Partial Tree from a Single Update Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(100).fill(null) + elements[97] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(256).fill(null) + tree[225] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[224] = '4ac05d0ec2e247aad1065f712d3d6934938e4709f224a0466f558bdf2e4cbf9c' + tree[112] = 'e7460e0eccc4403f9059c623ae43e560a0b6f4b9785d8a10415f969a8c2e43a9' + tree[113] = 'bbc26fa1ff8c9f841d4f4758cccac1def0f9929c30c949451d4e71e4ded0a681' + tree[56] = '6a14db7dc66e0be22f91e132c9e092e887dacd1a2526b9ca4c470d7f8734e2a3' + tree[28] = '6a14db7dc66e0be22f91e132c9e092e887dacd1a2526b9ca4c470d7f8734e2a3' + tree[14] = '6a14db7dc66e0be22f91e132c9e092e887dacd1a2526b9ca4c470d7f8734e2a3' + tree[7] = '6a14db7dc66e0be22f91e132c9e092e887dacd1a2526b9ca4c470d7f8734e2a3' + tree[6] = '06f8f83483a72750b8ba34cbe8fd54cc1243479b12f7b659075311dc54800203' + tree[3] = '933e92290b8e923d9e547b70303a61e667e4f03d4466de1ec26784453373f3b1' + tree[2] = 'eb98df4415ff9a93976bb26b84f3819662fe31939e022cfa52d9061de351f6d5' + tree[1] = '4ecd6f339dc6f43d8fa3de95e02a64f0ec5164e3c3efc0039971f0d26c5f3158' + tree[0] = 'cfad3bbd49206b0a95e99bc8142dbc721bfba301b92d58f333be397d8e7aafe6' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(100, 'ff', 97, expected, options) + }) + + it('should build a sorted-hash 9-element Partial Tree from a Single Update Proof.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + + const elements = Array(9).fill(null) + elements[8] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(32).fill(null) + tree[24] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[12] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[6] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[3] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[2] = '7f8dc34b7b4e06eff546283358ff8d7a988b62bc266f6337f8234c9a84778221' + tree[1] = 'f1a096a975a9f9513dfdb118bb20a97d8c295b6f0611dc0f534a3b6bec295936' + tree[0] = 'be0810314c2e27c5f5603badd29180a7b05c653610316a9a0c8c8a12a717714d' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(9, 'ff', 8, expected, options) + }) + + it('should build a sorted-hash 27-element Partial Tree from a Single Update Proof', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + + const elements = Array(27).fill(null) + elements[25] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(64).fill(null) + tree[57] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[56] = 'c43e6f0c51c26c040dc4a40e372839ccc4a53c1762504da451819ae9e93d239a' + tree[28] = 'd7fd8f16dd0c420b695ed34e12141c4530b65cd346a1d5cc1c077057a33a785b' + tree[29] = '289b3d65643b54739112fa7df258736b511ed1b5611e4f9ce681ae39fbd5fd8b' + tree[14] = '4c14cf08dc05a191b4f04fd7001fa1d71f0e617ae645fa2e8f93a11a042ad060' + tree[7] = '4c14cf08dc05a191b4f04fd7001fa1d71f0e617ae645fa2e8f93a11a042ad060' + tree[6] = 'c62e1d7cf122111fa068da94e48ecd21cb02bba4bd41d56e9f4b69a4509a2962' + tree[3] = 'e63eff8548c4a352c11fbbd2a1111b4526a98c3eb39eedd01c2b4345511de708' + tree[2] = '2c2cdc952c9d537709959cd357f6268fff33e5e21147f1a23db6cae78fb91eb9' + tree[1] = '521a2fa11c738870dec9c2519a45dfd866fb9b0a95b7cead92e432e6b353a921' + tree[0] = 'c3c247c16fb9d52178d83da6a3f85172060739de643923ec8acb692be839ff75' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(27, 'ff', 25, expected, options) + }) + + it('should build a sorted-hash 100-element Partial Tree from a Single Update Proof', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + + const elements = Array(100).fill(null) + elements[97] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(256).fill(null) + tree[225] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[224] = '4ac05d0ec2e247aad1065f712d3d6934938e4709f224a0466f558bdf2e4cbf9c' + tree[112] = 'e7460e0eccc4403f9059c623ae43e560a0b6f4b9785d8a10415f969a8c2e43a9' + tree[113] = 'bbc26fa1ff8c9f841d4f4758cccac1def0f9929c30c949451d4e71e4ded0a681' + tree[56] = '59b2383d7d0a521066520a275438e3d49d1f72c8950d0ac5caa665152f929670' + tree[28] = '59b2383d7d0a521066520a275438e3d49d1f72c8950d0ac5caa665152f929670' + tree[14] = '59b2383d7d0a521066520a275438e3d49d1f72c8950d0ac5caa665152f929670' + tree[7] = '59b2383d7d0a521066520a275438e3d49d1f72c8950d0ac5caa665152f929670' + tree[6] = '904afce76e0f7ccead463e22aec76018c1450afd3deb4f387e0617ef39721685' + tree[3] = '3766820de274be4c4a7c41b54218a5c04dfc58e539d219d86625548150d76e7e' + tree[2] = 'bb9a6e5787ae741c6a0e75a360aefe75ee06284ece1edddc1573ac9462945e7f' + tree[1] = '4c59bb7f57405275a4dae1f8c2ec3d0b8852d01c3570550c358f233b6b0685ce' + tree[0] = 'f315156d41b9e00a931aeff237c52b1f83ce87942bda42c0a7500d96e13ad980' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(100, 'ff', 97, expected, options) + }) + + it('should build a 100-element Partial Tree from a compact Single Update Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + + const elements = Array(100).fill(null) + elements[97] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(256).fill(null) + tree[225] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[224] = '4ac05d0ec2e247aad1065f712d3d6934938e4709f224a0466f558bdf2e4cbf9c' + tree[112] = 'e7460e0eccc4403f9059c623ae43e560a0b6f4b9785d8a10415f969a8c2e43a9' + tree[113] = 'bbc26fa1ff8c9f841d4f4758cccac1def0f9929c30c949451d4e71e4ded0a681' + tree[56] = '6a14db7dc66e0be22f91e132c9e092e887dacd1a2526b9ca4c470d7f8734e2a3' + tree[28] = '6a14db7dc66e0be22f91e132c9e092e887dacd1a2526b9ca4c470d7f8734e2a3' + tree[14] = '6a14db7dc66e0be22f91e132c9e092e887dacd1a2526b9ca4c470d7f8734e2a3' + tree[7] = '6a14db7dc66e0be22f91e132c9e092e887dacd1a2526b9ca4c470d7f8734e2a3' + tree[6] = '06f8f83483a72750b8ba34cbe8fd54cc1243479b12f7b659075311dc54800203' + tree[3] = '933e92290b8e923d9e547b70303a61e667e4f03d4466de1ec26784453373f3b1' + tree[2] = 'eb98df4415ff9a93976bb26b84f3819662fe31939e022cfa52d9061de351f6d5' + tree[1] = '4ecd6f339dc6f43d8fa3de95e02a64f0ec5164e3c3efc0039971f0d26c5f3158' + tree[0] = 'cfad3bbd49206b0a95e99bc8142dbc721bfba301b92d58f333be397d8e7aafe6' + + const expected = { elements, tree } + + testTreeFromSingleUpdateProof(100, 'ff', 97, expected, options) + }) + }) + }) + + describe('Build Partial Trees From Indexed Multi Proofs', () => { + describe('Balanced', () => { + it('should build an 8-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + + const elements = Array(8).fill(null) + elements[1] = 'ab28e51d2b2978f600476d733f1fb8688095ab06619ff948f4faf487a36d61be' + elements[4] = '86d370d74fa064564149838d4511fb57e4f4357b8fe925e79c400ee768015cc1' + elements[5] = '2deea3ad223102262743172859e6077fca0415f5825a11f88222ebe424d524f1' + + const tree = Array(16).fill(null) + tree[9] = '530371c484bd3476650533bc45fc8d339ccac500552b3c0ceea3e4aba4ffe3ac' + tree[8] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[12] = '5bcbf3cce3e9edf1556f976451ab1df564e24b2642521dbe2d21254bbeadbe80' + tree[13] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[6] = 'e98d0590a166e2e060560ff27987aef772fba0d1b553273fec591368d5640286' + tree[7] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[4] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[2] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[3] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[1] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[0] = 'd2fa9d47845f1571f1318afaaabc63a55cc57af3f511e42fc30e05f171d6853d' + + const expected = { elements, tree } + + testTreeFromMultiProof(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should build a 1-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + + const elements = Array(1).fill(null) + elements[0] = '06d41322d79dfed27126569cb9a80eb0967335bf2f3316359d2a93c779fcd38a' + + const tree = Array(2).fill(null) + tree[1] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[0] = 'c83b51dc238800a2852366108ab7df1e32b35e99905c5d845ff5a652f0fb58a8' + + const expected = { elements, tree } + + testTreeFromMultiProof(1, 'ff', [0], expected, options) + }) + + it('should build a sorted-hash 8-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: false } + + const elements = Array(8).fill(null) + elements[1] = 'ab28e51d2b2978f600476d733f1fb8688095ab06619ff948f4faf487a36d61be' + elements[4] = '86d370d74fa064564149838d4511fb57e4f4357b8fe925e79c400ee768015cc1' + elements[5] = '2deea3ad223102262743172859e6077fca0415f5825a11f88222ebe424d524f1' + + const tree = Array(16).fill(null) + tree[9] = '530371c484bd3476650533bc45fc8d339ccac500552b3c0ceea3e4aba4ffe3ac' + tree[8] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[12] = '5bcbf3cce3e9edf1556f976451ab1df564e24b2642521dbe2d21254bbeadbe80' + tree[13] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[6] = 'e98d0590a166e2e060560ff27987aef772fba0d1b553273fec591368d5640286' + tree[7] = 'df00f936e8f696ef3929c73d2176f2012336b0fd4fa5ae504bb3053a44993b94' + tree[4] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[2] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[3] = 'bc481a454a66b25fd1adf8b6b88cbcac3783d39d5ab1e4c45d114846da10274c' + tree[1] = '7f8dc34b7b4e06eff546283358ff8d7a988b62bc266f6337f8234c9a84778221' + tree[0] = '6764fd6d226590b844285c3d0f1e12bbd19cb7d1ee8277b0fb5b9b45efbbffb6' + + const expected = { elements, tree } + + testTreeFromMultiProof(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should build a sorted-hash 1-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: false } + + const elements = Array(1).fill(null) + elements[0] = '06d41322d79dfed27126569cb9a80eb0967335bf2f3316359d2a93c779fcd38a' + + const tree = Array(2).fill(null) + tree[1] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[0] = 'c83b51dc238800a2852366108ab7df1e32b35e99905c5d845ff5a652f0fb58a8' + + const expected = { elements, tree } + + testTreeFromMultiProof(1, 'ff', [0], expected, options) + }) + + it('should build an 8-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: true } + + const elements = Array(8).fill(null) + elements[1] = 'ab28e51d2b2978f600476d733f1fb8688095ab06619ff948f4faf487a36d61be' + elements[4] = '86d370d74fa064564149838d4511fb57e4f4357b8fe925e79c400ee768015cc1' + elements[5] = '2deea3ad223102262743172859e6077fca0415f5825a11f88222ebe424d524f1' + + const tree = Array(16).fill(null) + tree[9] = '530371c484bd3476650533bc45fc8d339ccac500552b3c0ceea3e4aba4ffe3ac' + tree[8] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[12] = '5bcbf3cce3e9edf1556f976451ab1df564e24b2642521dbe2d21254bbeadbe80' + tree[13] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[6] = 'e98d0590a166e2e060560ff27987aef772fba0d1b553273fec591368d5640286' + tree[7] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[4] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[2] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[3] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[1] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[0] = 'd2fa9d47845f1571f1318afaaabc63a55cc57af3f511e42fc30e05f171d6853d' + + const expected = { elements, tree } + + testTreeFromMultiProof(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should build a 1-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: true } + + const elements = Array(1).fill(null) + elements[0] = '06d41322d79dfed27126569cb9a80eb0967335bf2f3316359d2a93c779fcd38a' + + const tree = Array(2).fill(null) + tree[1] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[0] = 'c83b51dc238800a2852366108ab7df1e32b35e99905c5d845ff5a652f0fb58a8' + + const expected = { elements, tree } + + testTreeFromMultiProof(1, 'ff', [0], expected, options) + }) + }) + + describe('Unbalanced', () => { + it('should build a 12-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + + const elements = Array(12).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + elements[3] = 'd62f55381db89d56d428a8f383e2bb4690f27ef918d80b9f4735737f6abce3ec' + elements[8] = '137b5194eb17aa8cc50e40d7054133657e5e5e13a6a746694cc6d464993ea3f1' + elements[11] = '71677d75cd383c133f32314542bc7936d3d43561b5a5e04ac936310a81ae144b' + + const tree = Array(32).fill(null) + tree[18] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[19] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[24] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[25] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[27] = '817d36c43490240f07cc03207faed6536cad93b4c6da8dbdee91cf138af7b0c1' + tree[26] = '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964' + tree[9] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[8] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[12] = '8188a4ece78ff772c7986f979be6dc11cb8be4b007304fbc92fd03b373468e05' + tree[13] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[4] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[5] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[6] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[2] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[3] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[1] = '490333a663032866dc7e1d94b0dd889d3934c21be0d344c8c3df67a51ebc5176' + tree[0] = 'b2a2c912869453400b0080e31848ccbcabb264d8135bf55452479601f550cdc1' + + const expected = { elements, tree } + + testTreeFromMultiProof(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should build a 19-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + + const elements = Array(19).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + elements[4] = '86d370d74fa064564149838d4511fb57e4f4357b8fe925e79c400ee768015cc1' + elements[9] = 'd6ef9b1ab628977e002767025434cb4cc9fe91756355208938d306d4727e2962' + elements[12] = '9c3b4ba8006b4fc7148cc6eec4d4843f369c9aa6e639ec0c2d9e9574cffb12b2' + elements[17] = '34c287baaa9a48f13b5fa36a50c07e766febe977aece54114cae624e78fa335f' + + const tree = Array(64).fill(null) + tree[34] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[35] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[36] = '5bcbf3cce3e9edf1556f976451ab1df564e24b2642521dbe2d21254bbeadbe80' + tree[37] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[41] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[40] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[44] = '14a5443c720c2048f1c0d8a8884e7ef461f2bebd4850bb9576e019d7aca62122' + tree[45] = '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec' + tree[49] = '6d4884b8cca54ec5d50f49f171f2c2503bf2a592de38124441ad89c30cbca964' + tree[48] = '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9' + tree[17] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[16] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[18] = 'e98d0590a166e2e060560ff27987aef772fba0d1b553273fec591368d5640286' + tree[19] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[20] = '8188a4ece78ff772c7986f979be6dc11cb8be4b007304fbc92fd03b373468e05' + tree[21] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[22] = '712ed55abe1946b941876a6230b3135edadc400a18897e029ffdbff6900503e6' + tree[23] = 'd1c7a0741902e7210c324ee399555decfb24a0281c42d0a690a84acae5a1cfd2' + tree[24] = '370025ffa10a94cdc630db59f941fbc4b476e7002a81a358463cd0c180c52452' + tree[25] = 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445' + tree[8] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[9] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[10] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[11] = '611f780b080a4ea843420e043cf406d6336e10a6cbfcbbcb7cffd5e75dce2d3a' + tree[12] = '3e04893f45bdb92c2ae70a2fdfbea66b3ebbbb7bb02f656882deb4cb9062b228' + tree[4] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[5] = '951620fb6a7e785dd418bc6e672f36b8591fafe99a4a3bc7f15829d02d08326a' + tree[6] = '3e04893f45bdb92c2ae70a2fdfbea66b3ebbbb7bb02f656882deb4cb9062b228' + tree[2] = 'c7ec3e428ae2869b12c1b8e12a84e56f0d7f3cbe752cd1c3775158cf846412be' + tree[3] = '3e04893f45bdb92c2ae70a2fdfbea66b3ebbbb7bb02f656882deb4cb9062b228' + tree[1] = '7342698887d2969d39a68ff703c770b84c55ec5bddffe883f664f783c728342b' + tree[0] = '4259af49ecf3e6b5e2b50ab4e3624b18a13fcb74ad6528e4e112601067ee1c8b' + + const expected = { elements, tree } + + testTreeFromMultiProof(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + + it('should build a sorted-hash 12-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: false } + + const elements = Array(12).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + elements[3] = 'd62f55381db89d56d428a8f383e2bb4690f27ef918d80b9f4735737f6abce3ec' + elements[8] = '137b5194eb17aa8cc50e40d7054133657e5e5e13a6a746694cc6d464993ea3f1' + elements[11] = '71677d75cd383c133f32314542bc7936d3d43561b5a5e04ac936310a81ae144b' + + const tree = Array(32).fill(null) + tree[18] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[19] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[24] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[25] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[27] = '817d36c43490240f07cc03207faed6536cad93b4c6da8dbdee91cf138af7b0c1' + tree[26] = '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964' + tree[9] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[8] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[12] = '8188a4ece78ff772c7986f979be6dc11cb8be4b007304fbc92fd03b373468e05' + tree[13] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[4] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[5] = 'bc481a454a66b25fd1adf8b6b88cbcac3783d39d5ab1e4c45d114846da10274c' + tree[6] = '52759de88b47afb926b869b7e5e096797aa27796be32f087dc9e74c755270e5a' + tree[2] = '7f8dc34b7b4e06eff546283358ff8d7a988b62bc266f6337f8234c9a84778221' + tree[3] = '52759de88b47afb926b869b7e5e096797aa27796be32f087dc9e74c755270e5a' + tree[1] = '32f63ef925c871fdea1294f6443fc182bfef69c4ed42fd72a00e3cb5b48d84ca' + tree[0] = 'eed791e2f595179f11e8c2ac3457efb0e6374a6c9ae31f6fe6eeab9ddb5b2a92' + + const expected = { elements, tree } + + testTreeFromMultiProof(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should build a sorted-hash 19-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: false } + + const elements = Array(19).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + elements[4] = '86d370d74fa064564149838d4511fb57e4f4357b8fe925e79c400ee768015cc1' + elements[9] = 'd6ef9b1ab628977e002767025434cb4cc9fe91756355208938d306d4727e2962' + elements[12] = '9c3b4ba8006b4fc7148cc6eec4d4843f369c9aa6e639ec0c2d9e9574cffb12b2' + elements[17] = '34c287baaa9a48f13b5fa36a50c07e766febe977aece54114cae624e78fa335f' + + const tree = Array(64).fill(null) + tree[34] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[35] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[36] = '5bcbf3cce3e9edf1556f976451ab1df564e24b2642521dbe2d21254bbeadbe80' + tree[37] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[41] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[40] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[44] = '14a5443c720c2048f1c0d8a8884e7ef461f2bebd4850bb9576e019d7aca62122' + tree[45] = '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec' + tree[49] = '6d4884b8cca54ec5d50f49f171f2c2503bf2a592de38124441ad89c30cbca964' + tree[48] = '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9' + tree[17] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[16] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[18] = 'e98d0590a166e2e060560ff27987aef772fba0d1b553273fec591368d5640286' + tree[19] = 'df00f936e8f696ef3929c73d2176f2012336b0fd4fa5ae504bb3053a44993b94' + tree[20] = '8188a4ece78ff772c7986f979be6dc11cb8be4b007304fbc92fd03b373468e05' + tree[21] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[22] = '712ed55abe1946b941876a6230b3135edadc400a18897e029ffdbff6900503e6' + tree[23] = 'c83c0742945e25d8ea750b433deb383bd3c68c5e415398cb3a1bf7ebd760fe85' + tree[24] = '26e8df125e012fd185561da72d5b9c5ca6444f963465725f2b8d308b05219195' + tree[25] = 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445' + tree[8] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[9] = 'bc481a454a66b25fd1adf8b6b88cbcac3783d39d5ab1e4c45d114846da10274c' + tree[10] = '52759de88b47afb926b869b7e5e096797aa27796be32f087dc9e74c755270e5a' + tree[11] = 'ec5c9f8ce30c18531b0a15d3b5c40f4c77e8ceaa6280958d3396934a76db72e1' + tree[12] = '14bbf0000ea76dd757a7d2d05ac6ffd03ae26d0eb6e5560f6f05f6be5279fece' + tree[4] = '7f8dc34b7b4e06eff546283358ff8d7a988b62bc266f6337f8234c9a84778221' + tree[5] = '0374bacdc63c3ab3581bbcbea45292d9fada61d5620dc84c247a135b1045ab61' + tree[6] = '14bbf0000ea76dd757a7d2d05ac6ffd03ae26d0eb6e5560f6f05f6be5279fece' + tree[2] = '2c2cdc952c9d537709959cd357f6268fff33e5e21147f1a23db6cae78fb91eb9' + tree[3] = '14bbf0000ea76dd757a7d2d05ac6ffd03ae26d0eb6e5560f6f05f6be5279fece' + tree[1] = '72b124e49613cad1433e41be21c5d68336d9e7714a215c7eafa53e331435ed08' + tree[0] = 'bc3845d3df93dcdcf8648e95f63d693e328d07555ce259b6ff2420d6468cc6de' + + const expected = { elements, tree } + + testTreeFromMultiProof(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + + it('should build a 12-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: true } + + const elements = Array(12).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + elements[3] = 'd62f55381db89d56d428a8f383e2bb4690f27ef918d80b9f4735737f6abce3ec' + elements[8] = '137b5194eb17aa8cc50e40d7054133657e5e5e13a6a746694cc6d464993ea3f1' + elements[11] = '71677d75cd383c133f32314542bc7936d3d43561b5a5e04ac936310a81ae144b' + + const tree = Array(32).fill(null) + tree[18] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[19] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[24] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[25] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[27] = '817d36c43490240f07cc03207faed6536cad93b4c6da8dbdee91cf138af7b0c1' + tree[26] = '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964' + tree[9] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[8] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[12] = '8188a4ece78ff772c7986f979be6dc11cb8be4b007304fbc92fd03b373468e05' + tree[13] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[4] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[5] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[6] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[2] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[3] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[1] = '490333a663032866dc7e1d94b0dd889d3934c21be0d344c8c3df67a51ebc5176' + tree[0] = 'b2a2c912869453400b0080e31848ccbcabb264d8135bf55452479601f550cdc1' + + const expected = { elements, tree } + + testTreeFromMultiProof(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should build a 19-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: true } + + const elements = Array(19).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + elements[4] = '86d370d74fa064564149838d4511fb57e4f4357b8fe925e79c400ee768015cc1' + elements[9] = 'd6ef9b1ab628977e002767025434cb4cc9fe91756355208938d306d4727e2962' + elements[12] = '9c3b4ba8006b4fc7148cc6eec4d4843f369c9aa6e639ec0c2d9e9574cffb12b2' + elements[17] = '34c287baaa9a48f13b5fa36a50c07e766febe977aece54114cae624e78fa335f' + + const tree = Array(64).fill(null) + tree[34] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[35] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[36] = '5bcbf3cce3e9edf1556f976451ab1df564e24b2642521dbe2d21254bbeadbe80' + tree[37] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[41] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[40] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[44] = '14a5443c720c2048f1c0d8a8884e7ef461f2bebd4850bb9576e019d7aca62122' + tree[45] = '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec' + tree[49] = '6d4884b8cca54ec5d50f49f171f2c2503bf2a592de38124441ad89c30cbca964' + tree[48] = '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9' + tree[17] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[16] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[18] = 'e98d0590a166e2e060560ff27987aef772fba0d1b553273fec591368d5640286' + tree[19] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[20] = '8188a4ece78ff772c7986f979be6dc11cb8be4b007304fbc92fd03b373468e05' + tree[21] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[22] = '712ed55abe1946b941876a6230b3135edadc400a18897e029ffdbff6900503e6' + tree[23] = 'd1c7a0741902e7210c324ee399555decfb24a0281c42d0a690a84acae5a1cfd2' + tree[24] = '370025ffa10a94cdc630db59f941fbc4b476e7002a81a358463cd0c180c52452' + tree[25] = 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445' + tree[8] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[9] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[10] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[11] = '611f780b080a4ea843420e043cf406d6336e10a6cbfcbbcb7cffd5e75dce2d3a' + tree[12] = '3e04893f45bdb92c2ae70a2fdfbea66b3ebbbb7bb02f656882deb4cb9062b228' + tree[4] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[5] = '951620fb6a7e785dd418bc6e672f36b8591fafe99a4a3bc7f15829d02d08326a' + tree[6] = '3e04893f45bdb92c2ae70a2fdfbea66b3ebbbb7bb02f656882deb4cb9062b228' + tree[2] = 'c7ec3e428ae2869b12c1b8e12a84e56f0d7f3cbe752cd1c3775158cf846412be' + tree[3] = '3e04893f45bdb92c2ae70a2fdfbea66b3ebbbb7bb02f656882deb4cb9062b228' + tree[1] = '7342698887d2969d39a68ff703c770b84c55ec5bddffe883f664f783c728342b' + tree[0] = '4259af49ecf3e6b5e2b50ab4e3624b18a13fcb74ad6528e4e112601067ee1c8b' + + const expected = { elements, tree } + + testTreeFromMultiProof(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + }) + }) + + describe('Build Partial Trees From Indexed Multi Update Proofs', () => { + describe('Balanced', () => { + it('should build an 8-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + + const elements = Array(8).fill(null) + elements[1] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[4] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[5] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + + const tree = Array(16).fill(null) + tree[9] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[8] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[12] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[13] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[6] = 'c4d2af4260b1b6fb70da505eae59c017e85494d7347391314426863623b3be0b' + tree[7] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[4] = 'a46bdb4a7730f0ebf7d2f4149e0bbf066e4b52cbaeb0935fdebdcc75fa6ab56a' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[2] = '315eab4799ecc00535ac9167cc56fc4b5c3522b8c34bb540c6b11932ae6f3c16' + tree[3] = '68aaad4dc3fcf6e2be402ba9f23d2c4eefdaf2157085f3ad7d9e31ccc4223fef' + tree[1] = '70f4a2cdb70dc0a5af88bb2920eea7437ef147a760a4ab90d17cbee19aa615bd' + tree[0] = '88c85e371de22cd21498139441e9f7c9824dd59bda368080459ce0e9ebac8631' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should build a 1-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + + const elements = Array(1).fill(null) + elements[0] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(2).fill(null) + tree[1] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[0] = '2524b2088139a21ce5deaed4424127ad4efbd4859298df2a280d467154d4f898' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(1, 'ff', [0], expected, options) + }) + + it('should build a sorted-hash 8-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: false } + + const elements = Array(8).fill(null) + elements[1] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[4] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[5] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + + const tree = Array(16).fill(null) + tree[9] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[8] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[12] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[13] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[6] = 'c4d2af4260b1b6fb70da505eae59c017e85494d7347391314426863623b3be0b' + tree[7] = 'df00f936e8f696ef3929c73d2176f2012336b0fd4fa5ae504bb3053a44993b94' + tree[4] = 'a46bdb4a7730f0ebf7d2f4149e0bbf066e4b52cbaeb0935fdebdcc75fa6ab56a' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[2] = '315eab4799ecc00535ac9167cc56fc4b5c3522b8c34bb540c6b11932ae6f3c16' + tree[3] = 'dc386389404408833c276657d9fe7db12b11e27d5fb2f95c4c0a17cf76a01fe8' + tree[1] = 'd1e7fecf991fb3c3b8f7edd8ef1f070ba2851d6e01e0a9f49323d283ffed523b' + tree[0] = '77ccec5e6fae7c5453773196e6bd02fe2d5712b4fe8d3063850e5c2d8bd3fc7d' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should build a sorted-hash 1-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: false } + + const elements = Array(1).fill(null) + elements[0] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(2).fill(null) + tree[1] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[0] = '2524b2088139a21ce5deaed4424127ad4efbd4859298df2a280d467154d4f898' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(1, 'ff', [0], expected, options) + }) + + it('should build an 8-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: true } + + const elements = Array(8).fill(null) + elements[1] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[4] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[5] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + + const tree = Array(16).fill(null) + tree[9] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[8] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[12] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[13] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[6] = 'c4d2af4260b1b6fb70da505eae59c017e85494d7347391314426863623b3be0b' + tree[7] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[4] = 'a46bdb4a7730f0ebf7d2f4149e0bbf066e4b52cbaeb0935fdebdcc75fa6ab56a' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[2] = '315eab4799ecc00535ac9167cc56fc4b5c3522b8c34bb540c6b11932ae6f3c16' + tree[3] = '68aaad4dc3fcf6e2be402ba9f23d2c4eefdaf2157085f3ad7d9e31ccc4223fef' + tree[1] = '70f4a2cdb70dc0a5af88bb2920eea7437ef147a760a4ab90d17cbee19aa615bd' + tree[0] = '88c85e371de22cd21498139441e9f7c9824dd59bda368080459ce0e9ebac8631' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should build a 1-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: true } + + const elements = Array(1).fill(null) + elements[0] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(2).fill(null) + tree[1] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[0] = '2524b2088139a21ce5deaed4424127ad4efbd4859298df2a280d467154d4f898' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(1, 'ff', [0], expected, options) + }) + }) + + describe('Unbalanced', () => { + it('should build a 12-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + + const elements = Array(12).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[3] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[8] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + elements[11] = '1123274cbe0edaca6ea7391ca53726c872587203607100703231587a32334582' + + const tree = Array(32).fill(null) + tree[18] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[19] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[24] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[25] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[27] = '9b5965575d3e7eed294f422f06915719f50d6aeff18e4c00fe01f68a80db5182' + tree[26] = '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964' + tree[9] = 'bc74c504de9ea00fd578f493c5925b7e5d578882e05064d736b92098f1512fab' + tree[8] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[12] = 'd79560dcfc44d44df455b842c6d76ee777d270ccc091d76891a7a2b9294bc7da' + tree[13] = '1ba1c16e880fd8b0611f7212cb84b183215ec1034112e533c606c0294350d717' + tree[4] = '454836566a8bde3bc1db3d3b4b3309ac837ba762b7d09b197e0f6c18eb6ca76b' + tree[5] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[6] = 'c43b4c65f3d411131a89df6a4c75d0263b10610cb98bab39fe08965e84fba424' + tree[2] = '1fc27bba7c52e0c7d24d117d67ed095474fdf5f8a530d9a20b738ba06104ad63' + tree[3] = 'c43b4c65f3d411131a89df6a4c75d0263b10610cb98bab39fe08965e84fba424' + tree[1] = 'b1d75a44c3752c364cb7ddfd5fc51fd5e2ac342f1da0d886c1975f9b871e6369' + tree[0] = '0f99c39043f976c0fe669d3e77215f62315cc6eeaa127b2683a97c610a58af7b' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should build a 19-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + + const elements = Array(19).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[4] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[9] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + elements[12] = '1123274cbe0edaca6ea7391ca53726c872587203607100703231587a32334582' + elements[17] = 'f1ce0304c2dc475acda047ca28e42789de5200700771845a85d06db75aa69922' + + const tree = Array(64).fill(null) + tree[34] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[35] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[36] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[37] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[41] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[40] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[44] = '9b5965575d3e7eed294f422f06915719f50d6aeff18e4c00fe01f68a80db5182' + tree[45] = '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec' + tree[49] = '1ce248c80d46e0cc991a8fdc522368aa773464ce1b4af5a974c5dcb2696457ff' + tree[48] = '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9' + tree[17] = 'ec3bf1549ba1a76a9647403c4fbfa861de4a9bd594d3591f93a25597b5d4a373' + tree[16] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[18] = 'aa11fa4f96d1a23dc0613dfd7411b8ad57af1ef8d576cf090e39eebec662c247' + tree[19] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[20] = '8bfec52acb8389063c263b84d47b2b20bc2b26037df19b98436b4efba8508e61' + tree[21] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[22] = 'b62f8b3c28ad8f92d61c776ea2b6357842b63161b001480bcf023e16531574ca' + tree[23] = 'd1c7a0741902e7210c324ee399555decfb24a0281c42d0a690a84acae5a1cfd2' + tree[24] = '568c098c56e5f5ea1db8b951577c7f2e48d557aa6da100b2d336c73999781c16' + tree[25] = 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445' + tree[8] = '209b3596df36a23f39a11d2292acdf5075a68644f49393b3739e7b8ce99acb18' + tree[9] = '238013ac53085ccd5318b49e128ec2ffe2ac155aa83f4dce022e08a8066e768a' + tree[10] = '39d7e81a12bd3d8f29e0094177217197b1478ee1c8a0ef621553bf457a79d03f' + tree[11] = '2c6dd04728a13f6a6d46a679d5e405f1814053421621a18d41ab30d70d74d24e' + tree[12] = 'c7c184b7ae17914df88b164564e834c9d461845e76336aca451519f5e35ca817' + tree[4] = 'f91604c649be4018ef83d611ca16324b5f659dd0ebc8e35cd6ef59839ba2e7bd' + tree[5] = 'e4c34dfc909023ea3b2ca18b0fa0404fd9370db6c45470b50aed6a71356f01b6' + tree[6] = 'c7c184b7ae17914df88b164564e834c9d461845e76336aca451519f5e35ca817' + tree[2] = '7ab76ef90e60d8c0e03e814d405d4f0d6d1e65c942ee5a6a57cab8d308aa2979' + tree[3] = 'c7c184b7ae17914df88b164564e834c9d461845e76336aca451519f5e35ca817' + tree[1] = '37f8b7f288943dc7852efc8524fb68ad442cf882f984528bce1d2d4a8a83922e' + tree[0] = '993c6813836813058d359454c6356bf15f55d66bb7fa4a46fba7837c9317d874' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + + it('should build a sorted-hash 12-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: false } + + const elements = Array(12).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[3] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[8] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + elements[11] = '1123274cbe0edaca6ea7391ca53726c872587203607100703231587a32334582' + + const tree = Array(32).fill(null) + tree[18] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[19] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[24] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[25] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[27] = '9b5965575d3e7eed294f422f06915719f50d6aeff18e4c00fe01f68a80db5182' + tree[26] = '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964' + tree[9] = 'eea3f7291477c7084145e8efeb410a70bf2668dda075d72f0e50d586f6461b55' + tree[8] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[12] = 'd79560dcfc44d44df455b842c6d76ee777d270ccc091d76891a7a2b9294bc7da' + tree[13] = '1ba1c16e880fd8b0611f7212cb84b183215ec1034112e533c606c0294350d717' + tree[4] = '755475ac3df51625a4c35f31f25fa29cec5da917b6f58694aea50a8144617678' + tree[5] = 'bc481a454a66b25fd1adf8b6b88cbcac3783d39d5ab1e4c45d114846da10274c' + tree[6] = '86b7601cb096d7897832f51c3e137f5b2d5383825804d6edd4153453c08bc35c' + tree[2] = '960fd193fa81a56e77439a59c914692ecae86f1dc51591b728affde33e0303dd' + tree[3] = '86b7601cb096d7897832f51c3e137f5b2d5383825804d6edd4153453c08bc35c' + tree[1] = 'ab6f6ba5a701dbe1972d029ff3f68ba26f7f81fc788ecfbc2bfe7fff479e9f6b' + tree[0] = '406947a25a1b4cdf38a3fa21359248c643d7afa7bfd999540350d75bcfc0fa0c' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should build a sorted-hash 19-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: false } + + const elements = Array(19).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[4] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[9] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + elements[12] = '1123274cbe0edaca6ea7391ca53726c872587203607100703231587a32334582' + elements[17] = 'f1ce0304c2dc475acda047ca28e42789de5200700771845a85d06db75aa69922' + + const tree = Array(64).fill(null) + tree[34] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[35] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[36] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[37] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[41] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[40] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[44] = '9b5965575d3e7eed294f422f06915719f50d6aeff18e4c00fe01f68a80db5182' + tree[45] = '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec' + tree[49] = '1ce248c80d46e0cc991a8fdc522368aa773464ce1b4af5a974c5dcb2696457ff' + tree[48] = '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9' + tree[17] = 'ec3bf1549ba1a76a9647403c4fbfa861de4a9bd594d3591f93a25597b5d4a373' + tree[16] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[18] = 'aa11fa4f96d1a23dc0613dfd7411b8ad57af1ef8d576cf090e39eebec662c247' + tree[19] = 'df00f936e8f696ef3929c73d2176f2012336b0fd4fa5ae504bb3053a44993b94' + tree[20] = '8bfec52acb8389063c263b84d47b2b20bc2b26037df19b98436b4efba8508e61' + tree[21] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[22] = 'b62f8b3c28ad8f92d61c776ea2b6357842b63161b001480bcf023e16531574ca' + tree[23] = 'c83c0742945e25d8ea750b433deb383bd3c68c5e415398cb3a1bf7ebd760fe85' + tree[24] = '99214dccee2db79709f5817ef660c1bcdcb110c8919f0e1e3dbf5674cbc5d427' + tree[25] = 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445' + tree[8] = '209b3596df36a23f39a11d2292acdf5075a68644f49393b3739e7b8ce99acb18' + tree[9] = '6099d076fcf90a41189cf5ee9c7ff543fe77b284bc359dfeee84e272be310773' + tree[10] = 'b8e38c10e6b142a370b09e508956db85afe90ff350c255cff2ec0f1753fb27c0' + tree[11] = '3ef24b201cf26e765971ddee2803ebf2d1b9b4a19af57e8098c9b7bf7858b733' + tree[12] = '2f8da70a8cf7cf9ac83d871a9e70975ad314dc218cd5d7e6b83db6f3e79e058d' + tree[4] = '58258e4ca167669e6d477e985718937a916f7a76e73b21151bf5f704f1b9a3f4' + tree[5] = '1bc2e6b5af3d3c1681178b469c00ccee9d52fa4ad1ae48e889238701ce7e7cf7' + tree[6] = '2f8da70a8cf7cf9ac83d871a9e70975ad314dc218cd5d7e6b83db6f3e79e058d' + tree[2] = 'e3497591fb20950880fd86ce8254df903e55e481c8c930cb08a4d48ef768d5a1' + tree[3] = '2f8da70a8cf7cf9ac83d871a9e70975ad314dc218cd5d7e6b83db6f3e79e058d' + tree[1] = '7e13679d68dea01a85cd23c3f1c887913b566cc62eeae064e07300f3abf469d1' + tree[0] = 'f345ef2c8bc55630badfdb65f8762ae25b735cf2f4222307b823fc64070a10a0' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + + it('should build a 12-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: true } + + const elements = Array(12).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[3] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[8] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + elements[11] = '1123274cbe0edaca6ea7391ca53726c872587203607100703231587a32334582' + + const tree = Array(32).fill(null) + tree[18] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[19] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[24] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[25] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[27] = '9b5965575d3e7eed294f422f06915719f50d6aeff18e4c00fe01f68a80db5182' + tree[26] = '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964' + tree[9] = 'bc74c504de9ea00fd578f493c5925b7e5d578882e05064d736b92098f1512fab' + tree[8] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[12] = 'd79560dcfc44d44df455b842c6d76ee777d270ccc091d76891a7a2b9294bc7da' + tree[13] = '1ba1c16e880fd8b0611f7212cb84b183215ec1034112e533c606c0294350d717' + tree[4] = '454836566a8bde3bc1db3d3b4b3309ac837ba762b7d09b197e0f6c18eb6ca76b' + tree[5] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[6] = 'c43b4c65f3d411131a89df6a4c75d0263b10610cb98bab39fe08965e84fba424' + tree[2] = '1fc27bba7c52e0c7d24d117d67ed095474fdf5f8a530d9a20b738ba06104ad63' + tree[3] = 'c43b4c65f3d411131a89df6a4c75d0263b10610cb98bab39fe08965e84fba424' + tree[1] = 'b1d75a44c3752c364cb7ddfd5fc51fd5e2ac342f1da0d886c1975f9b871e6369' + tree[0] = '0f99c39043f976c0fe669d3e77215f62315cc6eeaa127b2683a97c610a58af7b' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should build a 19-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: true } + + const elements = Array(19).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[4] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[9] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + elements[12] = '1123274cbe0edaca6ea7391ca53726c872587203607100703231587a32334582' + elements[17] = 'f1ce0304c2dc475acda047ca28e42789de5200700771845a85d06db75aa69922' + + const tree = Array(64).fill(null) + tree[34] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[35] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[36] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[37] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[41] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[40] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[44] = '9b5965575d3e7eed294f422f06915719f50d6aeff18e4c00fe01f68a80db5182' + tree[45] = '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec' + tree[49] = '1ce248c80d46e0cc991a8fdc522368aa773464ce1b4af5a974c5dcb2696457ff' + tree[48] = '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9' + tree[17] = 'ec3bf1549ba1a76a9647403c4fbfa861de4a9bd594d3591f93a25597b5d4a373' + tree[16] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[18] = 'aa11fa4f96d1a23dc0613dfd7411b8ad57af1ef8d576cf090e39eebec662c247' + tree[19] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[20] = '8bfec52acb8389063c263b84d47b2b20bc2b26037df19b98436b4efba8508e61' + tree[21] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[22] = 'b62f8b3c28ad8f92d61c776ea2b6357842b63161b001480bcf023e16531574ca' + tree[23] = 'd1c7a0741902e7210c324ee399555decfb24a0281c42d0a690a84acae5a1cfd2' + tree[24] = '568c098c56e5f5ea1db8b951577c7f2e48d557aa6da100b2d336c73999781c16' + tree[25] = 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445' + tree[8] = '209b3596df36a23f39a11d2292acdf5075a68644f49393b3739e7b8ce99acb18' + tree[9] = '238013ac53085ccd5318b49e128ec2ffe2ac155aa83f4dce022e08a8066e768a' + tree[10] = '39d7e81a12bd3d8f29e0094177217197b1478ee1c8a0ef621553bf457a79d03f' + tree[11] = '2c6dd04728a13f6a6d46a679d5e405f1814053421621a18d41ab30d70d74d24e' + tree[12] = 'c7c184b7ae17914df88b164564e834c9d461845e76336aca451519f5e35ca817' + tree[4] = 'f91604c649be4018ef83d611ca16324b5f659dd0ebc8e35cd6ef59839ba2e7bd' + tree[5] = 'e4c34dfc909023ea3b2ca18b0fa0404fd9370db6c45470b50aed6a71356f01b6' + tree[6] = 'c7c184b7ae17914df88b164564e834c9d461845e76336aca451519f5e35ca817' + tree[2] = '7ab76ef90e60d8c0e03e814d405d4f0d6d1e65c942ee5a6a57cab8d308aa2979' + tree[3] = 'c7c184b7ae17914df88b164564e834c9d461845e76336aca451519f5e35ca817' + tree[1] = '37f8b7f288943dc7852efc8524fb68ad442cf882f984528bce1d2d4a8a83922e' + tree[0] = '993c6813836813058d359454c6356bf15f55d66bb7fa4a46fba7837c9317d874' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + }) + }) + + describe('Build Partial Trees From Existence-Only Multi Proofs', () => { + describe('Balanced', () => { + it('should build an 8-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + + const elements = Array(8).fill(null) + elements[1] = 'ab28e51d2b2978f600476d733f1fb8688095ab06619ff948f4faf487a36d61be' + elements[4] = '86d370d74fa064564149838d4511fb57e4f4357b8fe925e79c400ee768015cc1' + elements[5] = '2deea3ad223102262743172859e6077fca0415f5825a11f88222ebe424d524f1' + + const tree = Array(16).fill(null) + tree[9] = '530371c484bd3476650533bc45fc8d339ccac500552b3c0ceea3e4aba4ffe3ac' + tree[8] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[12] = '5bcbf3cce3e9edf1556f976451ab1df564e24b2642521dbe2d21254bbeadbe80' + tree[13] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[6] = 'e98d0590a166e2e060560ff27987aef772fba0d1b553273fec591368d5640286' + tree[7] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[4] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[2] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[3] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[1] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[0] = 'd2fa9d47845f1571f1318afaaabc63a55cc57af3f511e42fc30e05f171d6853d' + + const expected = { elements, tree } + + testTreeFromMultiProof(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should build a 1-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + + const elements = Array(1).fill(null) + elements[0] = '06d41322d79dfed27126569cb9a80eb0967335bf2f3316359d2a93c779fcd38a' + + const tree = Array(2).fill(null) + tree[1] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[0] = 'c83b51dc238800a2852366108ab7df1e32b35e99905c5d845ff5a652f0fb58a8' + + const expected = { elements, tree } + + testTreeFromMultiProof(1, 'ff', [0], expected, options) + }) + + it('should build an 8-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + + const elements = Array(8).fill(null) + elements[1] = 'ab28e51d2b2978f600476d733f1fb8688095ab06619ff948f4faf487a36d61be' + elements[4] = '86d370d74fa064564149838d4511fb57e4f4357b8fe925e79c400ee768015cc1' + elements[5] = '2deea3ad223102262743172859e6077fca0415f5825a11f88222ebe424d524f1' + + const tree = Array(16).fill(null) + tree[9] = '530371c484bd3476650533bc45fc8d339ccac500552b3c0ceea3e4aba4ffe3ac' + tree[8] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[12] = '5bcbf3cce3e9edf1556f976451ab1df564e24b2642521dbe2d21254bbeadbe80' + tree[13] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[6] = 'e98d0590a166e2e060560ff27987aef772fba0d1b553273fec591368d5640286' + tree[7] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[4] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[2] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[3] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[1] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[0] = 'd2fa9d47845f1571f1318afaaabc63a55cc57af3f511e42fc30e05f171d6853d' + + const expected = { elements, tree } + + testTreeFromMultiProof(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should build a 1-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + + const elements = Array(1).fill(null) + elements[0] = '06d41322d79dfed27126569cb9a80eb0967335bf2f3316359d2a93c779fcd38a' + + const tree = Array(2).fill(null) + tree[1] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[0] = 'c83b51dc238800a2852366108ab7df1e32b35e99905c5d845ff5a652f0fb58a8' + + const expected = { elements, tree } + + testTreeFromMultiProof(1, 'ff', [0], expected, options) + }) + }) + + describe('Unbalanced', () => { + it('should build a 12-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + + const elements = Array(12).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + elements[3] = 'd62f55381db89d56d428a8f383e2bb4690f27ef918d80b9f4735737f6abce3ec' + elements[8] = '137b5194eb17aa8cc50e40d7054133657e5e5e13a6a746694cc6d464993ea3f1' + elements[11] = '71677d75cd383c133f32314542bc7936d3d43561b5a5e04ac936310a81ae144b' + + const tree = Array(32).fill(null) + tree[18] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[19] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[24] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[25] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[27] = '817d36c43490240f07cc03207faed6536cad93b4c6da8dbdee91cf138af7b0c1' + tree[26] = '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964' + tree[9] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[8] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[12] = '8188a4ece78ff772c7986f979be6dc11cb8be4b007304fbc92fd03b373468e05' + tree[13] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[4] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[5] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[6] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[2] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[3] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[1] = '490333a663032866dc7e1d94b0dd889d3934c21be0d344c8c3df67a51ebc5176' + tree[0] = 'b2a2c912869453400b0080e31848ccbcabb264d8135bf55452479601f550cdc1' + + const expected = { elements, tree } + + testTreeFromMultiProof(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should build a 19-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + + const elements = Array(19).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + elements[4] = '86d370d74fa064564149838d4511fb57e4f4357b8fe925e79c400ee768015cc1' + elements[9] = 'd6ef9b1ab628977e002767025434cb4cc9fe91756355208938d306d4727e2962' + elements[12] = '9c3b4ba8006b4fc7148cc6eec4d4843f369c9aa6e639ec0c2d9e9574cffb12b2' + elements[17] = '34c287baaa9a48f13b5fa36a50c07e766febe977aece54114cae624e78fa335f' + + const tree = Array(64).fill(null) + tree[34] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[35] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[36] = '5bcbf3cce3e9edf1556f976451ab1df564e24b2642521dbe2d21254bbeadbe80' + tree[37] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[41] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[40] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[44] = '14a5443c720c2048f1c0d8a8884e7ef461f2bebd4850bb9576e019d7aca62122' + tree[45] = '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec' + tree[49] = '6d4884b8cca54ec5d50f49f171f2c2503bf2a592de38124441ad89c30cbca964' + tree[48] = '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9' + tree[17] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[16] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[18] = 'e98d0590a166e2e060560ff27987aef772fba0d1b553273fec591368d5640286' + tree[19] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[20] = '8188a4ece78ff772c7986f979be6dc11cb8be4b007304fbc92fd03b373468e05' + tree[21] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[22] = '712ed55abe1946b941876a6230b3135edadc400a18897e029ffdbff6900503e6' + tree[23] = 'd1c7a0741902e7210c324ee399555decfb24a0281c42d0a690a84acae5a1cfd2' + tree[24] = '370025ffa10a94cdc630db59f941fbc4b476e7002a81a358463cd0c180c52452' + tree[25] = 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445' + tree[8] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[9] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[10] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[11] = '611f780b080a4ea843420e043cf406d6336e10a6cbfcbbcb7cffd5e75dce2d3a' + tree[12] = '3e04893f45bdb92c2ae70a2fdfbea66b3ebbbb7bb02f656882deb4cb9062b228' + tree[4] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[5] = '951620fb6a7e785dd418bc6e672f36b8591fafe99a4a3bc7f15829d02d08326a' + tree[6] = '3e04893f45bdb92c2ae70a2fdfbea66b3ebbbb7bb02f656882deb4cb9062b228' + tree[2] = 'c7ec3e428ae2869b12c1b8e12a84e56f0d7f3cbe752cd1c3775158cf846412be' + tree[3] = '3e04893f45bdb92c2ae70a2fdfbea66b3ebbbb7bb02f656882deb4cb9062b228' + tree[1] = '7342698887d2969d39a68ff703c770b84c55ec5bddffe883f664f783c728342b' + tree[0] = '4259af49ecf3e6b5e2b50ab4e3624b18a13fcb74ad6528e4e112601067ee1c8b' + + const expected = { elements, tree } + + testTreeFromMultiProof(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + + it('should build a 12-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + + const elements = Array(12).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + elements[3] = 'd62f55381db89d56d428a8f383e2bb4690f27ef918d80b9f4735737f6abce3ec' + elements[8] = '137b5194eb17aa8cc50e40d7054133657e5e5e13a6a746694cc6d464993ea3f1' + elements[11] = '71677d75cd383c133f32314542bc7936d3d43561b5a5e04ac936310a81ae144b' + + const tree = Array(32).fill(null) + tree[18] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[19] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[24] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[25] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[27] = '817d36c43490240f07cc03207faed6536cad93b4c6da8dbdee91cf138af7b0c1' + tree[26] = '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964' + tree[9] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[8] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[12] = '8188a4ece78ff772c7986f979be6dc11cb8be4b007304fbc92fd03b373468e05' + tree[13] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[4] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[5] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[6] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[2] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[3] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[1] = '490333a663032866dc7e1d94b0dd889d3934c21be0d344c8c3df67a51ebc5176' + tree[0] = 'b2a2c912869453400b0080e31848ccbcabb264d8135bf55452479601f550cdc1' + + const expected = { elements, tree } + + testTreeFromMultiProof(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should build a 19-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + + const elements = Array(19).fill(null) + elements[2] = 'f7383164bf75f186fc5c5e69dfe6c2eaeb1e50a9568c379db8430b952daaa900' + elements[4] = '86d370d74fa064564149838d4511fb57e4f4357b8fe925e79c400ee768015cc1' + elements[9] = 'd6ef9b1ab628977e002767025434cb4cc9fe91756355208938d306d4727e2962' + elements[12] = '9c3b4ba8006b4fc7148cc6eec4d4843f369c9aa6e639ec0c2d9e9574cffb12b2' + elements[17] = '34c287baaa9a48f13b5fa36a50c07e766febe977aece54114cae624e78fa335f' + + const tree = Array(64).fill(null) + tree[34] = 'a7220cb76d040b2fdf4e25b319539c769eb77147fcb92b6ea8962cd04096c27b' + tree[35] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[36] = '5bcbf3cce3e9edf1556f976451ab1df564e24b2642521dbe2d21254bbeadbe80' + tree[37] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[41] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[40] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[44] = '14a5443c720c2048f1c0d8a8884e7ef461f2bebd4850bb9576e019d7aca62122' + tree[45] = '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec' + tree[49] = '6d4884b8cca54ec5d50f49f171f2c2503bf2a592de38124441ad89c30cbca964' + tree[48] = '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9' + tree[17] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[16] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[18] = 'e98d0590a166e2e060560ff27987aef772fba0d1b553273fec591368d5640286' + tree[19] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[20] = '8188a4ece78ff772c7986f979be6dc11cb8be4b007304fbc92fd03b373468e05' + tree[21] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[22] = '712ed55abe1946b941876a6230b3135edadc400a18897e029ffdbff6900503e6' + tree[23] = 'd1c7a0741902e7210c324ee399555decfb24a0281c42d0a690a84acae5a1cfd2' + tree[24] = '370025ffa10a94cdc630db59f941fbc4b476e7002a81a358463cd0c180c52452' + tree[25] = 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445' + tree[8] = '3fbb2f5778c21ef81190ef861605c8f6771a2a60561d7aed46d34349cc944241' + tree[9] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[10] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[11] = '611f780b080a4ea843420e043cf406d6336e10a6cbfcbbcb7cffd5e75dce2d3a' + tree[12] = '3e04893f45bdb92c2ae70a2fdfbea66b3ebbbb7bb02f656882deb4cb9062b228' + tree[4] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[5] = '951620fb6a7e785dd418bc6e672f36b8591fafe99a4a3bc7f15829d02d08326a' + tree[6] = '3e04893f45bdb92c2ae70a2fdfbea66b3ebbbb7bb02f656882deb4cb9062b228' + tree[2] = 'c7ec3e428ae2869b12c1b8e12a84e56f0d7f3cbe752cd1c3775158cf846412be' + tree[3] = '3e04893f45bdb92c2ae70a2fdfbea66b3ebbbb7bb02f656882deb4cb9062b228' + tree[1] = '7342698887d2969d39a68ff703c770b84c55ec5bddffe883f664f783c728342b' + tree[0] = '4259af49ecf3e6b5e2b50ab4e3624b18a13fcb74ad6528e4e112601067ee1c8b' + + const expected = { elements, tree } + + testTreeFromMultiProof(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + }) + }) + + describe('Build Partial Trees From Existence-Only Multi Update Proofs', () => { + describe('Balanced', () => { + it('should build an 8-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + + const elements = Array(8).fill(null) + elements[1] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[4] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[5] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + + const tree = Array(16).fill(null) + tree[9] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[8] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[12] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[13] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[6] = 'c4d2af4260b1b6fb70da505eae59c017e85494d7347391314426863623b3be0b' + tree[7] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[4] = 'a46bdb4a7730f0ebf7d2f4149e0bbf066e4b52cbaeb0935fdebdcc75fa6ab56a' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[2] = '315eab4799ecc00535ac9167cc56fc4b5c3522b8c34bb540c6b11932ae6f3c16' + tree[3] = '68aaad4dc3fcf6e2be402ba9f23d2c4eefdaf2157085f3ad7d9e31ccc4223fef' + tree[1] = '70f4a2cdb70dc0a5af88bb2920eea7437ef147a760a4ab90d17cbee19aa615bd' + tree[0] = '88c85e371de22cd21498139441e9f7c9824dd59bda368080459ce0e9ebac8631' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should build a 1-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + + const elements = Array(1).fill(null) + elements[0] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(2).fill(null) + tree[1] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[0] = '2524b2088139a21ce5deaed4424127ad4efbd4859298df2a280d467154d4f898' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(1, 'ff', [0], expected, options) + }) + + it('should build an 8-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + + const elements = Array(8).fill(null) + elements[1] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[4] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[5] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + + const tree = Array(16).fill(null) + tree[9] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[8] = '0e3ba1c61ffe3e984a50346034613b3b7368e64dafd5ea3d2ac05fc5ada33a60' + tree[12] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[13] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[6] = 'c4d2af4260b1b6fb70da505eae59c017e85494d7347391314426863623b3be0b' + tree[7] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[4] = 'a46bdb4a7730f0ebf7d2f4149e0bbf066e4b52cbaeb0935fdebdcc75fa6ab56a' + tree[5] = 'e868cc58247970644e689c7c207cdbbe6db49bec4953c7ba28527799056f07e9' + tree[2] = '315eab4799ecc00535ac9167cc56fc4b5c3522b8c34bb540c6b11932ae6f3c16' + tree[3] = '68aaad4dc3fcf6e2be402ba9f23d2c4eefdaf2157085f3ad7d9e31ccc4223fef' + tree[1] = '70f4a2cdb70dc0a5af88bb2920eea7437ef147a760a4ab90d17cbee19aa615bd' + tree[0] = '88c85e371de22cd21498139441e9f7c9824dd59bda368080459ce0e9ebac8631' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(8, 'ff', [1, 4, 5], expected, options) + }) + + it('should build a 1-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + + const elements = Array(1).fill(null) + elements[0] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + + const tree = Array(2).fill(null) + tree[1] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[0] = '2524b2088139a21ce5deaed4424127ad4efbd4859298df2a280d467154d4f898' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(1, 'ff', [0], expected, options) + }) + }) + + describe('Unbalanced', () => { + it('should build a 12-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + + const elements = Array(12).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[3] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[8] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + elements[11] = '1123274cbe0edaca6ea7391ca53726c872587203607100703231587a32334582' + + const tree = Array(32).fill(null) + tree[18] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[19] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[24] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[25] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[27] = '9b5965575d3e7eed294f422f06915719f50d6aeff18e4c00fe01f68a80db5182' + tree[26] = '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964' + tree[9] = 'bc74c504de9ea00fd578f493c5925b7e5d578882e05064d736b92098f1512fab' + tree[8] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[12] = 'd79560dcfc44d44df455b842c6d76ee777d270ccc091d76891a7a2b9294bc7da' + tree[13] = '1ba1c16e880fd8b0611f7212cb84b183215ec1034112e533c606c0294350d717' + tree[4] = '454836566a8bde3bc1db3d3b4b3309ac837ba762b7d09b197e0f6c18eb6ca76b' + tree[5] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[6] = 'c43b4c65f3d411131a89df6a4c75d0263b10610cb98bab39fe08965e84fba424' + tree[2] = '1fc27bba7c52e0c7d24d117d67ed095474fdf5f8a530d9a20b738ba06104ad63' + tree[3] = 'c43b4c65f3d411131a89df6a4c75d0263b10610cb98bab39fe08965e84fba424' + tree[1] = 'b1d75a44c3752c364cb7ddfd5fc51fd5e2ac342f1da0d886c1975f9b871e6369' + tree[0] = '0f99c39043f976c0fe669d3e77215f62315cc6eeaa127b2683a97c610a58af7b' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should build a 19-element Partial Tree from a Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: false } + + const elements = Array(19).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[4] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[9] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + elements[12] = '1123274cbe0edaca6ea7391ca53726c872587203607100703231587a32334582' + elements[17] = 'f1ce0304c2dc475acda047ca28e42789de5200700771845a85d06db75aa69922' + + const tree = Array(64).fill(null) + tree[34] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[35] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[36] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[37] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[41] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[40] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[44] = '9b5965575d3e7eed294f422f06915719f50d6aeff18e4c00fe01f68a80db5182' + tree[45] = '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec' + tree[49] = '1ce248c80d46e0cc991a8fdc522368aa773464ce1b4af5a974c5dcb2696457ff' + tree[48] = '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9' + tree[17] = 'ec3bf1549ba1a76a9647403c4fbfa861de4a9bd594d3591f93a25597b5d4a373' + tree[16] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[18] = 'aa11fa4f96d1a23dc0613dfd7411b8ad57af1ef8d576cf090e39eebec662c247' + tree[19] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[20] = '8bfec52acb8389063c263b84d47b2b20bc2b26037df19b98436b4efba8508e61' + tree[21] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[22] = 'b62f8b3c28ad8f92d61c776ea2b6357842b63161b001480bcf023e16531574ca' + tree[23] = 'd1c7a0741902e7210c324ee399555decfb24a0281c42d0a690a84acae5a1cfd2' + tree[24] = '568c098c56e5f5ea1db8b951577c7f2e48d557aa6da100b2d336c73999781c16' + tree[25] = 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445' + tree[8] = '209b3596df36a23f39a11d2292acdf5075a68644f49393b3739e7b8ce99acb18' + tree[9] = '238013ac53085ccd5318b49e128ec2ffe2ac155aa83f4dce022e08a8066e768a' + tree[10] = '39d7e81a12bd3d8f29e0094177217197b1478ee1c8a0ef621553bf457a79d03f' + tree[11] = '2c6dd04728a13f6a6d46a679d5e405f1814053421621a18d41ab30d70d74d24e' + tree[12] = 'c7c184b7ae17914df88b164564e834c9d461845e76336aca451519f5e35ca817' + tree[4] = 'f91604c649be4018ef83d611ca16324b5f659dd0ebc8e35cd6ef59839ba2e7bd' + tree[5] = 'e4c34dfc909023ea3b2ca18b0fa0404fd9370db6c45470b50aed6a71356f01b6' + tree[6] = 'c7c184b7ae17914df88b164564e834c9d461845e76336aca451519f5e35ca817' + tree[2] = '7ab76ef90e60d8c0e03e814d405d4f0d6d1e65c942ee5a6a57cab8d308aa2979' + tree[3] = 'c7c184b7ae17914df88b164564e834c9d461845e76336aca451519f5e35ca817' + tree[1] = '37f8b7f288943dc7852efc8524fb68ad442cf882f984528bce1d2d4a8a83922e' + tree[0] = '993c6813836813058d359454c6356bf15f55d66bb7fa4a46fba7837c9317d874' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + + it('should build a 12-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + + const elements = Array(12).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[3] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[8] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + elements[11] = '1123274cbe0edaca6ea7391ca53726c872587203607100703231587a32334582' + + const tree = Array(32).fill(null) + tree[18] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[19] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[24] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[25] = 'f9c38f9fec674389962b1b4cb3e26191be58cf5850ee58e4fe170b94de04d3d7' + tree[27] = '9b5965575d3e7eed294f422f06915719f50d6aeff18e4c00fe01f68a80db5182' + tree[26] = '4847e055cdb073d232313d8bf813dd31b7a3626d8e7881304d3bc41a848bf964' + tree[9] = 'bc74c504de9ea00fd578f493c5925b7e5d578882e05064d736b92098f1512fab' + tree[8] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[12] = 'd79560dcfc44d44df455b842c6d76ee777d270ccc091d76891a7a2b9294bc7da' + tree[13] = '1ba1c16e880fd8b0611f7212cb84b183215ec1034112e533c606c0294350d717' + tree[4] = '454836566a8bde3bc1db3d3b4b3309ac837ba762b7d09b197e0f6c18eb6ca76b' + tree[5] = 'babbf2a0bca3f1360d7706d6d175f3380c5973df4b2d1bb19a9496792891697d' + tree[6] = 'c43b4c65f3d411131a89df6a4c75d0263b10610cb98bab39fe08965e84fba424' + tree[2] = '1fc27bba7c52e0c7d24d117d67ed095474fdf5f8a530d9a20b738ba06104ad63' + tree[3] = 'c43b4c65f3d411131a89df6a4c75d0263b10610cb98bab39fe08965e84fba424' + tree[1] = 'b1d75a44c3752c364cb7ddfd5fc51fd5e2ac342f1da0d886c1975f9b871e6369' + tree[0] = '0f99c39043f976c0fe669d3e77215f62315cc6eeaa127b2683a97c610a58af7b' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(12, 'ff', [2, 3, 8, 11], expected, options) + }) + + it('should build a 19-element Partial Tree from a Compact Multi Proof.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + + const elements = Array(19).fill(null) + elements[2] = '4a8efaf9728aab687dd27244d1090ea0c6897fbf666dea4c60524cd49862342d' + elements[4] = 'a70e41111425aef1bf65b11de58118ec31949d6b8f1d9bdabb2667013076af8e' + elements[9] = '105a21d552f9585b75df6435729fc31417460aeea5a428bf134aed34347d94bc' + elements[12] = '1123274cbe0edaca6ea7391ca53726c872587203607100703231587a32334582' + elements[17] = 'f1ce0304c2dc475acda047ca28e42789de5200700771845a85d06db75aa69922' + + const tree = Array(64).fill(null) + tree[34] = '6e608355cf92a680b5f5992103414eb9a2c655214d7c9a865c03941346cf77ff' + tree[35] = 'c91b0c977258a25a3803b772c6229444bdca5f73995a108cf36439fdbb30d82e' + tree[36] = '6bd09ddd0038de358d2f4c761b2fa46378bb724b69099c7504fcc7f6db7aa73d' + tree[37] = '731017481bc7111f6621c3d3f5511e941e264077817c1939403ec0e16f24d24d' + tree[41] = 'c414008ffde0a24cccd3d881adcab80ac55ab632e12884c39c402b9f6694ed72' + tree[40] = 'c3ed5f33f97f302e5667c4cf731a7dca902aa88b1520976d37b79e2f614d839f' + tree[44] = '9b5965575d3e7eed294f422f06915719f50d6aeff18e4c00fe01f68a80db5182' + tree[45] = '9c14306519dcde45d6985845e9af155bc2431d1ad6fde29a957eaf464f7ed1ec' + tree[49] = '1ce248c80d46e0cc991a8fdc522368aa773464ce1b4af5a974c5dcb2696457ff' + tree[48] = '9a2ac3e4dd11303cc187ad6cbcfb99bcb955f3a242e14001a940a360d41abaa9' + tree[17] = 'ec3bf1549ba1a76a9647403c4fbfa861de4a9bd594d3591f93a25597b5d4a373' + tree[16] = 'a3ce89c3f749bfd79ce683054de83f70e40e847cef70e5389167871c4dd4af27' + tree[18] = 'aa11fa4f96d1a23dc0613dfd7411b8ad57af1ef8d576cf090e39eebec662c247' + tree[19] = '55e76a40d8624a05e93c6bbdd36ed61989ef5f0903cea080e9968b590ba30c39' + tree[20] = '8bfec52acb8389063c263b84d47b2b20bc2b26037df19b98436b4efba8508e61' + tree[21] = '23d7e3b895db705a5867f3e2d747351226d34734b355fdbcf3a7e99e688c6cb2' + tree[22] = 'b62f8b3c28ad8f92d61c776ea2b6357842b63161b001480bcf023e16531574ca' + tree[23] = 'd1c7a0741902e7210c324ee399555decfb24a0281c42d0a690a84acae5a1cfd2' + tree[24] = '568c098c56e5f5ea1db8b951577c7f2e48d557aa6da100b2d336c73999781c16' + tree[25] = 'df30b2c34f6b8879381b45dcc32c84248dc5119ecc364f00939660bbf5430445' + tree[8] = '209b3596df36a23f39a11d2292acdf5075a68644f49393b3739e7b8ce99acb18' + tree[9] = '238013ac53085ccd5318b49e128ec2ffe2ac155aa83f4dce022e08a8066e768a' + tree[10] = '39d7e81a12bd3d8f29e0094177217197b1478ee1c8a0ef621553bf457a79d03f' + tree[11] = '2c6dd04728a13f6a6d46a679d5e405f1814053421621a18d41ab30d70d74d24e' + tree[12] = 'c7c184b7ae17914df88b164564e834c9d461845e76336aca451519f5e35ca817' + tree[4] = 'f91604c649be4018ef83d611ca16324b5f659dd0ebc8e35cd6ef59839ba2e7bd' + tree[5] = 'e4c34dfc909023ea3b2ca18b0fa0404fd9370db6c45470b50aed6a71356f01b6' + tree[6] = 'c7c184b7ae17914df88b164564e834c9d461845e76336aca451519f5e35ca817' + tree[2] = '7ab76ef90e60d8c0e03e814d405d4f0d6d1e65c942ee5a6a57cab8d308aa2979' + tree[3] = 'c7c184b7ae17914df88b164564e834c9d461845e76336aca451519f5e35ca817' + tree[1] = '37f8b7f288943dc7852efc8524fb68ad442cf882f984528bce1d2d4a8a83922e' + tree[0] = '993c6813836813058d359454c6356bf15f55d66bb7fa4a46fba7837c9317d874' + + const expected = { elements, tree } + + testTreeFromMultiUpdateProof(19, 'ff', [2, 4, 9, 12, 17], expected, options) + }) + }) + }) + + describe('Build Partial Trees From Single Append Proofs', () => { + it('should build a 1-element Partial Tree from a Single Append Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(1).fill(null) + elements[0] = '2392a80f8a87b8cfde0aa5c84e199f163aae4c2a4c512d37598362ace687ad0c' + + const tree = Array(2).fill(null) + tree[1] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[0] = '9a98370dc2f17b2d0cd30197183a876bf73ab761360b8f2d053f9371aa897198' + + const expected = { elements, tree } + + testTreeFromSingleAppendProof(0, 'ff', expected, options) + }) + + it('should build a 9-element Partial Tree from a Single Append Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(9).fill(null) + elements[8] = '2392a80f8a87b8cfde0aa5c84e199f163aae4c2a4c512d37598362ace687ad0c' + + const tree = Array(32).fill(null) + tree[24] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[12] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[6] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[3] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[2] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[1] = 'd6108bc543a9fe3153c9f38a81cf65129438a37b041d6471f87de9ddffd2294d' + tree[0] = '644d289b64508601322e13302b9f7ee12152a5d96cd2b4a685980be591fa5518' + + const expected = { elements, tree } + + testTreeFromSingleAppendProof(8, 'ff', expected, options) + }) + + it('should build a 28-element Partial Tree from a Single Append Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(27).fill(null) + elements[27] = '2392a80f8a87b8cfde0aa5c84e199f163aae4c2a4c512d37598362ace687ad0c' + + const tree = Array(64).fill(null) + tree[59] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[58] = '289b3d65643b54739112fa7df258736b511ed1b5611e4f9ce681ae39fbd5fd8b' + tree[29] = '18f318057d7e5b343f2e2f47236b8bedd81dae9d328ccd167a835ed9ffbea309' + tree[28] = 'dd1c66ff9c67d05f1aeb7ec6196210db830b83ac973345b2908d31677d52c311' + tree[14] = '055671cd93095cfe5dd1e988b49163267248a47989e227ce86433a18d9d033ac' + tree[7] = '055671cd93095cfe5dd1e988b49163267248a47989e227ce86433a18d9d033ac' + tree[6] = '88d2a11c3b0935fc6a30e3b0a69fa58a84de08ea333248f23e5d747613fc04f9' + tree[3] = 'de97a499abd824d28794ccfe20d7636f76ec6608a02723da6c9ddf9cf97081e8' + tree[2] = 'c7ec3e428ae2869b12c1b8e12a84e56f0d7f3cbe752cd1c3775158cf846412be' + tree[1] = '9374f999ee0d756bc27b2fe08e8738016a384788611491c6f3462a04bd291654' + tree[0] = '555b456b2b77e2c8c1c6de55867f61d361616648a386cc27cb8066b10b697bb0' + + const expected = { elements, tree } + + testTreeFromSingleAppendProof(27, 'ff', expected, options) + }) + + it('should build a 100-element Partial Tree from a Single Append Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(100).fill(null) + elements[99] = '2392a80f8a87b8cfde0aa5c84e199f163aae4c2a4c512d37598362ace687ad0c' + + const tree = Array(256).fill(null) + tree[227] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[226] = '1989275de5f46e101747e3ebdd4f3440f26ddf1ede904e8bce47483cd22a3964' + tree[113] = '7cb2f6389e732511ec26d3dc26d1baefb1517033b0a09e207562b9a28972f5bf' + tree[112] = 'd902920fde7efe9ec00bd50e4195851294dcb7178a0aa8c5ebc913d5659586f9' + tree[56] = '2e317cad6cc387718cdd86a4c94bd61f13959d63d9ec83d6ce75f795c1cdbdf5' + tree[28] = '2e317cad6cc387718cdd86a4c94bd61f13959d63d9ec83d6ce75f795c1cdbdf5' + tree[14] = '2e317cad6cc387718cdd86a4c94bd61f13959d63d9ec83d6ce75f795c1cdbdf5' + tree[7] = '2e317cad6cc387718cdd86a4c94bd61f13959d63d9ec83d6ce75f795c1cdbdf5' + tree[6] = '06f8f83483a72750b8ba34cbe8fd54cc1243479b12f7b659075311dc54800203' + tree[3] = 'a5bd6391af59382143f8ddfe96aab7c0f263b8365312feb5c89e3f4098a7ef80' + tree[2] = 'eb98df4415ff9a93976bb26b84f3819662fe31939e022cfa52d9061de351f6d5' + tree[1] = 'f81322b778ada525c93c316f0e61d643cf42d924d9bc4c767c31c8c8fac25e6c' + tree[0] = '642bfc0c90349fe1439ef299a7d0d6bbf2251423690e0eef7c50bd8cdd3c1f5b' + + const expected = { elements, tree } + + testTreeFromSingleAppendProof(99, 'ff', expected, options) + }) + + it('should build a sorted-hash 100-element Partial Tree from a Single Append Proof', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + + const elements = Array(100).fill(null) + elements[99] = '2392a80f8a87b8cfde0aa5c84e199f163aae4c2a4c512d37598362ace687ad0c' + + const tree = Array(256).fill(null) + tree[227] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[226] = '1989275de5f46e101747e3ebdd4f3440f26ddf1ede904e8bce47483cd22a3964' + tree[113] = '7cb2f6389e732511ec26d3dc26d1baefb1517033b0a09e207562b9a28972f5bf' + tree[112] = 'd902920fde7efe9ec00bd50e4195851294dcb7178a0aa8c5ebc913d5659586f9' + tree[56] = 'fcc80b7a7ac922f505b7921e706e9953e4a00fd95694699101f7fe59958c87f6' + tree[28] = 'fcc80b7a7ac922f505b7921e706e9953e4a00fd95694699101f7fe59958c87f6' + tree[14] = 'fcc80b7a7ac922f505b7921e706e9953e4a00fd95694699101f7fe59958c87f6' + tree[7] = 'fcc80b7a7ac922f505b7921e706e9953e4a00fd95694699101f7fe59958c87f6' + tree[6] = '904afce76e0f7ccead463e22aec76018c1450afd3deb4f387e0617ef39721685' + tree[3] = '3d5036f4c1b91632e1cd835e67fc36be94bebadd0b9b4d8e70584b1afa6ff516' + tree[2] = 'bb9a6e5787ae741c6a0e75a360aefe75ee06284ece1edddc1573ac9462945e7f' + tree[1] = '5627182766e36a68230bfbe5ab8e384c79a6b476e408880adb04ca2623979443' + tree[0] = '51505aa103ba1261b0d3fbd820e7b3c86e914725fc1fc6785f1a19c62550cc02' + + const expected = { elements, tree } + + testTreeFromSingleAppendProof(99, 'ff', expected, options) + }) + + it('should build a 100-element Partial Tree from a Single Compact Append Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + + const elements = Array(100).fill(null) + elements[99] = '2392a80f8a87b8cfde0aa5c84e199f163aae4c2a4c512d37598362ace687ad0c' + + const tree = Array(256).fill(null) + tree[227] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[226] = '1989275de5f46e101747e3ebdd4f3440f26ddf1ede904e8bce47483cd22a3964' + tree[113] = '7cb2f6389e732511ec26d3dc26d1baefb1517033b0a09e207562b9a28972f5bf' + tree[112] = 'd902920fde7efe9ec00bd50e4195851294dcb7178a0aa8c5ebc913d5659586f9' + tree[56] = '2e317cad6cc387718cdd86a4c94bd61f13959d63d9ec83d6ce75f795c1cdbdf5' + tree[28] = '2e317cad6cc387718cdd86a4c94bd61f13959d63d9ec83d6ce75f795c1cdbdf5' + tree[14] = '2e317cad6cc387718cdd86a4c94bd61f13959d63d9ec83d6ce75f795c1cdbdf5' + tree[7] = '2e317cad6cc387718cdd86a4c94bd61f13959d63d9ec83d6ce75f795c1cdbdf5' + tree[6] = '06f8f83483a72750b8ba34cbe8fd54cc1243479b12f7b659075311dc54800203' + tree[3] = 'a5bd6391af59382143f8ddfe96aab7c0f263b8365312feb5c89e3f4098a7ef80' + tree[2] = 'eb98df4415ff9a93976bb26b84f3819662fe31939e022cfa52d9061de351f6d5' + tree[1] = 'f81322b778ada525c93c316f0e61d643cf42d924d9bc4c767c31c8c8fac25e6c' + tree[0] = '642bfc0c90349fe1439ef299a7d0d6bbf2251423690e0eef7c50bd8cdd3c1f5b' + + const expected = { elements, tree } + + testTreeFromSingleAppendProof(99, 'ff', expected, options) + }) + }) + + describe('Build Partial Trees From Multi Append Proofs', () => { + it('should build a 5-element Partial Tree from a Multi Append Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(5).fill(null) + elements[0] = '2392a80f8a87b8cfde0aa5c84e199f163aae4c2a4c512d37598362ace687ad0c' + elements[1] = 'f092ffc50adec70f320c33704d9a635c09529fc5b277deeb50068790f953f21d' + elements[2] = '136fc3f650545532c02974f1a68e700ef3b050b825ba02622c2aea300757cb7a' + elements[3] = '4a1a64e888466fdf89e30fddd3644715a620680373ed29a0856a24de11ab31b6' + elements[4] = 'cc099046f9453d6a1f75c54e5f0af530c46ab8af18752a50bddc7d1414d83233' + + const tree = Array(16).fill(null) + tree[8] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[9] = 'f00703443cdb74322917b11b412841d069e09846b5a9eddff1c47d6154d67bf1' + tree[10] = '0bdc458cd85cb58a583bca5e2cb8f10c6774ab4b5970f8fd7e49fb75149b3084' + tree[11] = 'bfc3b72b4829c12396a700234ff8fe6ad0a85091a4a0a6e2f5054f55933c2971' + tree[12] = 'ed8385437e7424a4d0dcdeefc91d3387facf4132df77ed4d19d5785511a451ba' + tree[4] = '21efd0fe292c1c3bf3ca9d0fcb6db213568fd2aa3ab676b407b3c3d9c3ca762b' + tree[5] = '31ee92d1cd117bf78d1eb0e8e347cf9d94fcee761e9f4094d7f35ab0641e3569' + tree[6] = 'ed8385437e7424a4d0dcdeefc91d3387facf4132df77ed4d19d5785511a451ba' + tree[2] = '257da86e84b3f26f04bbb59b200765ab720918d1379dc7a39be4da07de51fd34' + tree[3] = 'ed8385437e7424a4d0dcdeefc91d3387facf4132df77ed4d19d5785511a451ba' + tree[1] = '7e34c0358800ea0942afde971a7734ad73bca330dc253708e7b31834860b2e60' + tree[0] = '5506559aa2662c0729c3908e7dda087cd35df8271ecd066bc3e1ccf4c224bc24' + + const expected = { elements, tree } + + testTreeFromMultiAppendProof(0, 'ff', 5, expected, options) + }) + + it('should build a 19-element Partial Tree from a Multi Append Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(19).fill(null) + elements[15] = '2392a80f8a87b8cfde0aa5c84e199f163aae4c2a4c512d37598362ace687ad0c' + elements[16] = 'f092ffc50adec70f320c33704d9a635c09529fc5b277deeb50068790f953f21d' + elements[17] = '136fc3f650545532c02974f1a68e700ef3b050b825ba02622c2aea300757cb7a' + elements[18] = '4a1a64e888466fdf89e30fddd3644715a620680373ed29a0856a24de11ab31b6' + + const tree = Array(64).fill(null) + tree[47] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[46] = 'e481ff292c1b323f27dd2e7b0da511947e0d349a0616a739ea628a3a5888c529' + tree[48] = 'f00703443cdb74322917b11b412841d069e09846b5a9eddff1c47d6154d67bf1' + tree[49] = '0bdc458cd85cb58a583bca5e2cb8f10c6774ab4b5970f8fd7e49fb75149b3084' + tree[50] = 'bfc3b72b4829c12396a700234ff8fe6ad0a85091a4a0a6e2f5054f55933c2971' + tree[23] = '3730bf1994595bc03d335a15990afc3012d14959861661f72f9d6b73b3b0a104' + tree[22] = '712ed55abe1946b941876a6230b3135edadc400a18897e029ffdbff6900503e6' + tree[24] = '4d017b73ca099fe09575ea52c23db2b92f5feaa49eae575902292db5be07fae9' + tree[25] = 'bfc3b72b4829c12396a700234ff8fe6ad0a85091a4a0a6e2f5054f55933c2971' + tree[11] = '416152b456c9bd10dde787f8d5f628de547c3bd3b48d540ec9962e2d614f6d84' + tree[10] = 'd9df67e21f45396a2739193be4bb49cefb1ebac44dd283c07519b6de6f154f5b' + tree[12] = '8fa587374b458e8501d59f29466f3c04a5aaa589704efad5e9ca99d2ba7bef9c' + tree[5] = '8e13dcdaa050cc9e2972df4ecc208c788668979f0051c712f44cc339009ddc32' + tree[4] = '0c67c6340449c320fb4966988f319713e0610c40237a05fdef8e5da8c66db8a4' + tree[6] = '8fa587374b458e8501d59f29466f3c04a5aaa589704efad5e9ca99d2ba7bef9c' + tree[2] = '030b03ae56a1599ce857670375af01dd561e9653eb2206269f87cc7f00df9444' + tree[3] = '8fa587374b458e8501d59f29466f3c04a5aaa589704efad5e9ca99d2ba7bef9c' + tree[1] = 'e30366e6f5be486cb931dd35ff2bcc9cb8736adbbf530aac306b534c7275609b' + tree[0] = '0a328b9a33c61b836826e3b5e95cb3d75731a88848140f9ab8dcef38da71b8f1' + + const expected = { elements, tree } + + testTreeFromMultiAppendProof(15, 'ff', 4, expected, options) + }) + + it('should build a 100-element Partial Tree from a Multi Append Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + + const elements = Array(100).fill(null) + elements[98] = '2392a80f8a87b8cfde0aa5c84e199f163aae4c2a4c512d37598362ace687ad0c' + elements[99] = 'f092ffc50adec70f320c33704d9a635c09529fc5b277deeb50068790f953f21d' + + const tree = Array(256).fill(null) + tree[226] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[227] = 'f00703443cdb74322917b11b412841d069e09846b5a9eddff1c47d6154d67bf1' + tree[113] = '21efd0fe292c1c3bf3ca9d0fcb6db213568fd2aa3ab676b407b3c3d9c3ca762b' + tree[112] = 'd902920fde7efe9ec00bd50e4195851294dcb7178a0aa8c5ebc913d5659586f9' + tree[56] = 'a76508346a711aad11c7efa5eb14073f3bf5ea7ce4cc807f2546b5c3d8725913' + tree[28] = 'a76508346a711aad11c7efa5eb14073f3bf5ea7ce4cc807f2546b5c3d8725913' + tree[14] = 'a76508346a711aad11c7efa5eb14073f3bf5ea7ce4cc807f2546b5c3d8725913' + tree[7] = 'a76508346a711aad11c7efa5eb14073f3bf5ea7ce4cc807f2546b5c3d8725913' + tree[6] = '06f8f83483a72750b8ba34cbe8fd54cc1243479b12f7b659075311dc54800203' + tree[3] = '2baef9bd920a3e6e55d0a376c4c0598c723896f8422cc9f112314ed52f97fc95' + tree[2] = 'eb98df4415ff9a93976bb26b84f3819662fe31939e022cfa52d9061de351f6d5' + tree[1] = '2686a1bae53b5f4486536a06364fe51a9039a34b832907d043b23b4a5ec101bd' + tree[0] = '19b3427b04a2bc1988448d50754fe428f481e5a718f80b1ebc21c63d33c9ab19' + + const expected = { elements, tree } + + testTreeFromMultiAppendProof(98, 'ff', 2, expected, options) + }) + + it('should build a sorted-hash 100-element Partial Tree from a Multi Append Proof', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + + const elements = Array(100).fill(null) + elements[98] = '2392a80f8a87b8cfde0aa5c84e199f163aae4c2a4c512d37598362ace687ad0c' + elements[99] = 'f092ffc50adec70f320c33704d9a635c09529fc5b277deeb50068790f953f21d' + + const tree = Array(256).fill(null) + tree[226] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[227] = 'f00703443cdb74322917b11b412841d069e09846b5a9eddff1c47d6154d67bf1' + tree[113] = '21efd0fe292c1c3bf3ca9d0fcb6db213568fd2aa3ab676b407b3c3d9c3ca762b' + tree[112] = 'd902920fde7efe9ec00bd50e4195851294dcb7178a0aa8c5ebc913d5659586f9' + tree[56] = 'f9b6ac5ffcf84b497de6ae6906999e142f4a549df473b8c658366d9e6715394d' + tree[28] = 'f9b6ac5ffcf84b497de6ae6906999e142f4a549df473b8c658366d9e6715394d' + tree[14] = 'f9b6ac5ffcf84b497de6ae6906999e142f4a549df473b8c658366d9e6715394d' + tree[7] = 'f9b6ac5ffcf84b497de6ae6906999e142f4a549df473b8c658366d9e6715394d' + tree[6] = '904afce76e0f7ccead463e22aec76018c1450afd3deb4f387e0617ef39721685' + tree[3] = 'cd57129b7e48770f2bb207f4af549dc665f0f13462923741d702f93976eab525' + tree[2] = 'bb9a6e5787ae741c6a0e75a360aefe75ee06284ece1edddc1573ac9462945e7f' + tree[1] = 'f98e17df060961a9c4f1df54a070e11d9c5d346503cf795ddad3154cd4b92f3d' + tree[0] = '49e8bf2f73f5ffa15f9c97f0d113ad72851781460bcc163486d1fa01bc80762f' + + const expected = { elements, tree } + + testTreeFromMultiAppendProof(98, 'ff', 2, expected, options) + }) + + it('should build a 100-element Partial Tree from a Multi Compact Append Proof', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + + const elements = Array(100).fill(null) + elements[98] = '2392a80f8a87b8cfde0aa5c84e199f163aae4c2a4c512d37598362ace687ad0c' + elements[99] = 'f092ffc50adec70f320c33704d9a635c09529fc5b277deeb50068790f953f21d' + + const tree = Array(256).fill(null) + tree[226] = 'cc7539397f3b3dc92978e04e2d170afa35354687ac3b56079cf760f0eed009d0' + tree[227] = 'f00703443cdb74322917b11b412841d069e09846b5a9eddff1c47d6154d67bf1' + tree[113] = '21efd0fe292c1c3bf3ca9d0fcb6db213568fd2aa3ab676b407b3c3d9c3ca762b' + tree[112] = 'd902920fde7efe9ec00bd50e4195851294dcb7178a0aa8c5ebc913d5659586f9' + tree[56] = 'a76508346a711aad11c7efa5eb14073f3bf5ea7ce4cc807f2546b5c3d8725913' + tree[28] = 'a76508346a711aad11c7efa5eb14073f3bf5ea7ce4cc807f2546b5c3d8725913' + tree[14] = 'a76508346a711aad11c7efa5eb14073f3bf5ea7ce4cc807f2546b5c3d8725913' + tree[7] = 'a76508346a711aad11c7efa5eb14073f3bf5ea7ce4cc807f2546b5c3d8725913' + tree[6] = '06f8f83483a72750b8ba34cbe8fd54cc1243479b12f7b659075311dc54800203' + tree[3] = '2baef9bd920a3e6e55d0a376c4c0598c723896f8422cc9f112314ed52f97fc95' + tree[2] = 'eb98df4415ff9a93976bb26b84f3819662fe31939e022cfa52d9061de351f6d5' + tree[1] = '2686a1bae53b5f4486536a06364fe51a9039a34b832907d043b23b4a5ec101bd' + tree[0] = '19b3427b04a2bc1988448d50754fe428f481e5a718f80b1ebc21c63d33c9ab19' + + const expected = { elements, tree } + + testTreeFromMultiAppendProof(98, 'ff', 2, expected, options) + }) + }) + + describe('Generate Single Proofs From Partial Trees', () => { + describe('Balanced', () => { + it('should generate a Single Proof for a 8-element Partial Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + testGenerateSingleProofFromPartial(8, 2, options) + }) + + it('should generate a Single Proof for a 1-element Partial Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + testGenerateSingleProofFromPartial(1, 0, options) + }) + + it('should generate a Single Proof for a sorted-hash 8-element Partial Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + testGenerateSingleProofFromPartial(8, 2, options) + }) + + it('should generate a Single Proof for a sorted-hash 1-element Partial Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + testGenerateSingleProofFromPartial(1, 0, options) + }) + + it('should generate a compact Single Proof for a 8-element Partial Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + testGenerateSingleProofFromPartial(8, 2, options) + }) + + it('should generate a compact Single Proof for a 1-element Partial Merkle Tree.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + testGenerateSingleProofFromPartial(1, 0, options) + }) + }) + + describe('Unbalanced', () => { + it('should generate a Single Proof for a 9-element Partial Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testGenerateSingleProofFromPartial(9, 8, options) + }) + + it('should generate a Single Proof for a 27-element Partial Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testGenerateSingleProofFromPartial(27, 25, options) + }) + + it('should generate a Single Proof for a 100-element Partial Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testGenerateSingleProofFromPartial(100, 97, options) + }) + + it('should generate a Single Proof for a sorted-hash 9-element Partial Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testGenerateSingleProofFromPartial(9, 8, options) + }) + + it('should generate a Single Proof for a sorted-hash 27-element Partial Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testGenerateSingleProofFromPartial(27, 25, options) + }) + + it('should generate a Single Proof for a sorted-hash 100-element Partial Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testGenerateSingleProofFromPartial(100, 97, options) + }) + + it('should generate a compact Single Proof for a 100-element Partial Merkle Tree.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testGenerateSingleProofFromPartial(100, 97, options) + }) + }) + }) + + describe('Generate Single Update Proofs From Partial Trees', () => { + describe('Balanced', () => { + it('should generate a Single Proof for a 8-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + testGenerateSingleUpdateProofFromSinglePartial(8, 2, options) + }) + + it('should generate a Single Proof for a 1-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: false, sortedHash: false, compact: false } + testGenerateSingleUpdateProofFromSinglePartial(1, 0, options) + }) + + it('should generate a Single Proof for a sorted-hash 8-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + testGenerateSingleUpdateProofFromSinglePartial(8, 2, options) + }) + + it('should generate a Single Proof for a sorted-hash 1-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: false, sortedHash: true, compact: false } + testGenerateSingleUpdateProofFromSinglePartial(1, 0, options) + }) + + it('should generate a compact Single Proof for a 8-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + testGenerateSingleUpdateProofFromSinglePartial(8, 2, options) + }) + + it('should generate a compact Single Proof for a 1-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: false, sortedHash: false, compact: true } + testGenerateSingleUpdateProofFromSinglePartial(1, 0, options) + }) + }) + + describe('Unbalanced', () => { + it('should generate a Single Proof for a 9-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testGenerateSingleUpdateProofFromSinglePartial(9, 8, options) + }) + + it('should generate a Single Proof for a 27-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testGenerateSingleUpdateProofFromSinglePartial(27, 25, options) + }) + + it('should generate a Single Proof for a 100-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testGenerateSingleUpdateProofFromSinglePartial(100, 97, options) + }) + + it('should generate a Single Proof for a sorted-hash 9-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testGenerateSingleUpdateProofFromSinglePartial(9, 8, options) + }) + + it('should generate a Single Proof for a sorted-hash 27-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testGenerateSingleUpdateProofFromSinglePartial(27, 25, options) + }) + + it('should generate a Single Proof for a sorted-hash 100-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: true, sortedHash: true, compact: false } + testGenerateSingleUpdateProofFromSinglePartial(100, 97, options) + }) + + it('should generate a compact Single Proof for a 100-element Partial Merkle Tree to update an element.', () => { + const options = { unbalanced: true, sortedHash: false, compact: true } + testGenerateSingleUpdateProofFromSinglePartial(100, 97, options) + }) + }) + }) + + describe.skip('Generate Multi Proofs From Partial Trees (Redundant)', () => { }) + + describe('Generate Multi Update Proofs From Partial Trees', () => { + describe('Balanced', () => { + it('should generate an Indexed Multi Proof for a 8-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + testGenerateMultiUpdateProofFromMultiPartial(8, [1, 4, 5], options) + }) + + it('should generate an Indexed Multi Proof for a 1-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: true, compact: false } + testGenerateMultiUpdateProofFromMultiPartial(1, [0], options) + }) + + it('should generate an Indexed Multi Proof for a sorted-hash 8-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: false } + testGenerateMultiUpdateProofFromMultiPartial(8, [1, 4, 5], options) + }) + + it('should generate an Indexed Multi Proof for a sorted-hash 1-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: true, indexed: true, compact: false } + testGenerateMultiUpdateProofFromMultiPartial(1, [0], options) + }) + + it('should generate a Existence-Only Multi Proof for a 8-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + testGenerateMultiUpdateProofFromMultiPartial(8, [1, 4, 5], options) + }) + + it('should generate a Existence-Only Multi Proof for a 1-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: false } + testGenerateMultiUpdateProofFromMultiPartial(1, [0], options) + }) + + it('should generate a Compact Existence-Only Multi Proof for a 8-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + testGenerateMultiUpdateProofFromMultiPartial(8, [1, 4, 5], options) + }) + + it('should generate a Compact Existence-Only Multi Proof for a 1-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: false, sortedHash: false, indexed: false, compact: true } + testGenerateMultiUpdateProofFromMultiPartial(1, [0], options) + }) + }) + + describe('Unbalanced', () => { + it('should generate an Indexed Multi Proof for a 9-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + testGenerateMultiUpdateProofFromMultiPartial(9, [1, 8], options) + }) + + it('should generate an Indexed Multi Proof for a 27-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + testGenerateMultiUpdateProofFromMultiPartial(27, [5, 12, 20, 25], options) + }) + + it('should generate an Indexed Multi Proof for a 100-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: true, compact: false } + testGenerateMultiUpdateProofFromMultiPartial(100, [7, 24, 25, 68, 97], options) + }) + + it('should generate a Compact Indexed Multi Proof for a sorted-hash 9-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: true } + testGenerateMultiUpdateProofFromMultiPartial(9, [1, 8], options) + }) + + it('should generate a Compact Indexed Multi Proof for a sorted-hash 27-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: true } + testGenerateMultiUpdateProofFromMultiPartial(27, [5, 12, 20, 25], options) + }) + + it('should generate a Compact Indexed Multi Proof for a sorted-hash 100-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: true, sortedHash: true, indexed: true, compact: true } + testGenerateMultiUpdateProofFromMultiPartial(100, [7, 24, 25, 68, 97], options) + }) + + it('should generate Compact Existence-Only Multi Proof for a 9-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testGenerateMultiUpdateProofFromMultiPartial(9, [1, 8], options) + }) + + it('should generate Compact Existence-Only Multi Proof for a 27-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testGenerateMultiUpdateProofFromMultiPartial(27, [5, 12, 20, 25], options) + }) + + it('should generate Compact Existence-Only Multi Proof for a 100-element Partial Merkle Tree to update elements.', () => { + const options = { unbalanced: true, sortedHash: false, indexed: false, compact: true } + testGenerateMultiUpdateProofFromMultiPartial(100, [7, 24, 25, 68, 97], options) + }) + }) + }) + + describe('Check Elements in a Partial Tree', () => { + it('check elements in an 8-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testCheckElements(8, 2, [0, 2, 3, 7], [false, true, true, false], options) + }) + + it('check elements in an 89-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testCheckElements(89, 42, [0, 9, 42, 43, 87], [false, false, true, true, false], options) + }) + }) + + describe('Set Elements in a Partial Tree', () => { + it('sets an element in an 8-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSetElement(8, 2, 3, options) + }) + + it('sets an element in an 66-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSetElement(66, 41, 40, options) + }) + + it('sets an element in an 89-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSetElement(89, 41, 40, options) + }) + + it('sets elements in an 8-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSetElements(8, 2, [0, 1, 3], options) + }) + + it('sets elements in an 66-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSetElements(66, 41, [40, 42, 43], options) + }) + + it('sets elements in an 89-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testSetElements(89, 41, [40, 42, 43], options) + }) + }) + + describe('Appends Elements to a Partial Tree', () => { + it('appends an element to an 1-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendElement(1, options) + }) + + it('appends an element to an 8-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendElement(8, options) + }) + + it('appends an element to an 89-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendElement(89, options) + }) + + it('appends 12 elements to an 1-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendElements(1, 12, options) + }) + + it('appends 5 elements to an 8-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendElements(8, 5, options) + }) + + it('appends 41 elements to an 89-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendElements(89, 41, options) + }) + + it('appends 160 elements to an 24-element Partial Tree built from a Single Proof.', () => { + const options = { unbalanced: true, sortedHash: false, compact: false } + testAppendElements(89, 160, options) + }) + }) + + describe.skip('Arbitrary Element Sizes (Likely Redundant Given Merkle Tree Tests)', () => { }) +}) diff --git a/js/src/ts/utils.ts b/js/src/ts/utils.ts new file mode 100644 index 0000000..f6c0574 --- /dev/null +++ b/js/src/ts/utils.ts @@ -0,0 +1,73 @@ +import { Keccak } from "sha3" + +export const leftPad = (num: string | number, size: number, char: string = '0'): string => { + let s: string = num + '' + while (s.length < size) s = char + s + return s +} + +export const to32ByteBuffer = (num: number): Buffer => Buffer.from(leftPad(num.toString(16), 64), 'hex') + +export const from32ByteBuffer = (buffer: Buffer): number => buffer.readUInt32BE(28) + +export const bitCount32 = (n: number): number => { + let m = n - ((n >>> 1) & 0x55555555) + m = (m & 0x33333333) + ((m >>> 2) & 0x33333333) + + return (((m + (m >>> 4)) & 0xf0f0f0f) * 0x1010101) >>> 24 +} + +export const prefix = (value: string): string => (value.startsWith('0x') ? value : '0x' + value) + +export const hash = (buffer: Buffer): Buffer => new Keccak(256).update(buffer).digest() + +export const hashNode = (left: Buffer, right: Buffer): Buffer => hash(Buffer.concat([left, right])) + +export const sortHashNode = (left: Buffer, right: Buffer): Buffer => { + if (left && right) { + return hash(Buffer.concat([left, right].sort(Buffer.compare))) + } + + throw new Error('Both buffers must exist to be sorted and hashed.') +} + +export const getHashFunction = (sortedHash: boolean): ((x: Buffer, y: Buffer) => Buffer) => { + return (left, right: Buffer): Buffer => sortedHash ? sortHashNode(left, right) : hashNode(left, right) +} + +export const findLastIndex = (array: Array, predicate: (value: any, index: number, array: Array) => boolean): number => { + let i = array.length + + while (i--) { + if (predicate(array[i], i, array)) return i + } + + return -1 +} + +export const to32ByteBoolBuffer = (booleans: Array<1 | 0>): Buffer => { + if (booleans.length > 256) return null + const value = booleans.reduce((value, bool, i) => value | ((bool ? BigInt(1) : BigInt(0)) << BigInt(i)), BigInt(0)) + return Buffer.from(leftPad(value.toString(16), 64), 'hex') +} + +export const toBigIntBoolSet = (booleans: Array<1 | 0>): bigint => { + if (booleans.length > 256) return null + return booleans.reduce((value, bool, i) => value | ((bool ? BigInt(1) : BigInt(0)) << BigInt(i)), BigInt(0)) +} + +export const roundUpToPowerOf2 = (num: number): number => { + if (bitCount32(num) === 1) return num + + num |= num >>> 1 + num |= num >>> 2 + num |= num >>> 4 + num |= num >>> 8 + num |= num >>> 16 + + return num + 1 +} + +export const bigIntTo32ByteBuffer = (value: bigint): Buffer => Buffer.from(leftPad(value.toString(16), 64), 'hex') + +export const bufferToBigInt = (buffer: Buffer): bigint => BigInt('0x' + buffer.toString('hex')) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9ab3bde --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3466 @@ +{ + "name": "merkle-trees", + "version": "0.2.3", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", + "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@solidity-parser/parser": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.8.2.tgz", + "integrity": "sha512-8LySx3qrNXPgB5JiULfG10O3V7QTxI/TLzSw5hFQhXWSkVxZBAv4rZQ0sYgLEbc8g3L2lmnujj1hKul38Eu5NQ==", + "dev": true + }, + "@types/chai": { + "version": "4.2.18", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.18.tgz", + "integrity": "sha512-rS27+EkB/RE1Iz3u0XtVL5q36MGDWbgYe7zWiodyKNUnthxY0rukK5V36eiUCtCisB7NN8zKYH6DO2M37qxFEQ==", + "dev": true + }, + "@types/mocha": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", + "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", + "dev": true + }, + "@types/node": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.0.tgz", + "integrity": "sha512-gCYSfQpy+LYhOFTKAeE8BkyGqaxmlFxe+n4DKM6DR0wzw/HISUE/hAmkC/KT8Sw5PCJblqg062b3z9gucv3k0A==", + "dev": true + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "antlr4": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.7.1.tgz", + "integrity": "sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ==", + "dev": true + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "app-module-path": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", + "integrity": "sha1-ZBqlXft9am8KgUHEucCqULbCTdU=", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array.prototype.map": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.3.tgz", + "integrity": "sha512-nNcb30v0wfDyIe26Yif3PcV1JXQp4zEeEfupG7L4SRjnD6HLbO5b2a7eVSba53bOx4YCHYMBHt+Fp4vYstneRA==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.5" + } + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "ast-parents": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/ast-parents/-/ast-parents-0.0.1.tgz", + "integrity": "sha1-UI/Q8F0MSHddnszaLhdEIyYejdM=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz", + "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dir-to-object": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-to-object/-/dir-to-object-2.0.0.tgz", + "integrity": "sha512-sXs0JKIhymON7T1UZuO2Ud6VTNAx/VTBXIl4+3mjb2RgfOpt+hectX0x04YqPOPdkeOAKoJuKqwqnXXURNPNEA==", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", + "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.2", + "is-string": "^1.0.5", + "object-inspect": "^1.9.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.0" + }, + "dependencies": { + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "es-get-iterator": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", + "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.0", + "has-symbols": "^1.0.1", + "is-arguments": "^1.1.0", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "dev": true, + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "ganache-cli": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/ganache-cli/-/ganache-cli-6.12.1.tgz", + "integrity": "sha512-zoefZLQpQyEJH9jgtVYgM+ENFLAC9iwys07IDCsju2Ieq9KSTLH89RxSP4bhizXKV/h/+qaWpfyCBGWnBfqgIQ==", + "dev": true, + "requires": { + "ethereumjs-util": "6.2.1", + "source-map-support": "0.5.12", + "yargs": "13.2.4" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "14.11.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", + "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==", + "dev": true + }, + "@types/pbkdf2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", + "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/secp256k1": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.1.tgz", + "integrity": "sha512-+ZjSA8ELlOp8SlKi0YLB2tz9d5iPNEmOBd+8Rz21wTMdaXQIa9b6TEnD6l5qKOCypE7FSyPyck12qZJxSDNoog==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "base-x": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz", + "integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "blakejs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz", + "integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U=", + "dev": true + }, + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "dev": true, + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dev": true, + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "requires": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dev": true, + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "keccak": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz", + "integrity": "sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA==", + "dev": true, + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true + }, + "node-gyp-build": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", + "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rlp": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.6.tgz", + "integrity": "sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg==", + "dev": true, + "requires": { + "bn.js": "^4.11.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "dev": true + }, + "secp256k1": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", + "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", + "dev": true, + "requires": { + "elliptic": "^6.5.2", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", + "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "dev": true, + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + } + } + }, + "is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "dev": true + }, + "is-date-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", + "dev": true + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + } + }, + "is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true + }, + "is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", + "dev": true + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "iterate-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", + "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", + "dev": true + }, + "iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "dev": true, + "requires": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "mocha": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", + "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.4.3", + "debug": "4.2.0", + "diff": "4.0.2", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.2", + "nanoid": "3.1.12", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "7.2.0", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.0.2", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "2.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nanoid": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", + "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "original-require": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/original-require/-/original-require-1.0.1.tgz", + "integrity": "sha1-DxMEcVhM0zURxew4yNWSE/msXiA=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, + "prettier-plugin-solidity": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.1.tgz", + "integrity": "sha512-kfPkR+UscT/Cw+2O8RSh6gCnIY4qsJJtuE4xZpIq42EyNyTLBabDwjH1QocXHwmlgL6QffydDge76ERlyJRaAA==", + "dev": true, + "requires": { + "@solidity-parser/parser": "^0.9.1", + "dir-to-object": "^2.0.0", + "emoji-regex": "^9.0.0", + "escape-string-regexp": "^4.0.0", + "prettier": "^2.0.5", + "semver": "^7.3.2", + "solidity-comments-extractor": "^0.0.4", + "string-width": "^4.2.0" + }, + "dependencies": { + "@solidity-parser/parser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.9.1.tgz", + "integrity": "sha512-ewNo+ZEQX8mFUOlTK6+0IYvM++6+iEeRBIBg4Mh8ghgRX72bkXJh6AWLWe/SG5+3WPdDL84MSsAlrvWFsGRdFw==", + "dev": true, + "requires": { + "antlr4": "^4.8.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "antlr4": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.9.2.tgz", + "integrity": "sha512-UjMSlenUORL+a+6g4RNZxRh5LcFWybRi2g0ASDBpgXBY6nlavg0BRVAVEQF0dz8jH6SyX3lV7uP5y/krJzc+Hw==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.0.tgz", + "integrity": "sha512-DNc3KFPK18bPdElMJnf/Pkv5TXhxFU3YFDEuGLDRtPmV4rkmCjBkCSEp22u6rBHdSN9Vlp/GK7k98prmE1Jgug==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + } + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "sha3": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.3.tgz", + "integrity": "sha512-Io53D4o9qOmf3Ow9p/DoGLQiQHhtuR0ulbyambvRSG+OX5yXExk2yYfvjHtb7AtOyk6K6+sPeK/qaowWc/E/GA==", + "requires": { + "buffer": "5.6.0" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "solhint": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-3.3.2.tgz", + "integrity": "sha512-8tHCkIAk1axLLG6Qu2WIH3GgNABonj9eAWejJbov3o3ujkZQRNHeHU1cC4/Dmjsh3Om7UzFFeADUHu2i7ZJeiw==", + "dev": true, + "requires": { + "@solidity-parser/parser": "^0.8.2", + "ajv": "^6.6.1", + "antlr4": "4.7.1", + "ast-parents": "0.0.1", + "chalk": "^2.4.2", + "commander": "2.18.0", + "cosmiconfig": "^5.0.7", + "eslint": "^5.6.0", + "fast-diff": "^1.1.2", + "glob": "^7.1.3", + "ignore": "^4.0.6", + "js-yaml": "^3.12.0", + "lodash": "^4.17.11", + "prettier": "^1.14.3", + "semver": "^6.3.0" + }, + "dependencies": { + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true, + "optional": true + } + } + }, + "solidity-comments-extractor": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.4.tgz", + "integrity": "sha512-58glBODwXIKMaQ7rfcJOrWtFQMMOK28tJ0/LcB5Xhu7WtAxk4UX2fpgKPuaL41XjMp/y0gAa1MTLqk018wuSzA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "truffle": { + "version": "5.1.56", + "resolved": "https://registry.npmjs.org/truffle/-/truffle-5.1.56.tgz", + "integrity": "sha512-hmRcHZqrOF5vlc0s4CoM72mRMRgP/jObK57ixfDAri+oLRgHgIKBf98R7nsy0ruq+WHCAdq1jslmrF026lUxAw==", + "dev": true, + "requires": { + "app-module-path": "^2.2.0", + "mocha": "8.1.2", + "original-require": "1.0.1" + }, + "dependencies": { + "chokidar": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", + "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "flat": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", + "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + } + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "mocha": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.2.tgz", + "integrity": "sha512-I8FRAcuACNMLQn3lS4qeWLxXqLvGf6r2CaLstDpZmMUUSmvW6Cnm1AuHxgbc7ctZVRcfwspCRbDHymPsi3dkJw==", + "dev": true, + "requires": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.4.2", + "debug": "4.1.1", + "diff": "4.0.2", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.2", + "object.assign": "4.1.0", + "promise.allsettled": "1.0.2", + "serialize-javascript": "4.0.0", + "strip-json-comments": "3.0.1", + "supports-color": "7.1.0", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.0.0", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.1" + }, + "dependencies": { + "promise.allsettled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", + "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", + "dev": true, + "requires": { + "array.prototype.map": "^1.0.1", + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "iterate-value": "^1.0.0" + } + } + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "workerpool": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", + "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", + "dev": true + }, + "yargs-unparser": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz", + "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "decamelize": "^1.2.0", + "flat": "^4.1.0", + "is-plain-obj": "^1.1.0", + "yargs": "^14.2.3" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "yargs": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "yargs-parser": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", + "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + } + } + }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "dev": true + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "workerpool": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", + "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + } + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json index 6111f4f..625aa9c 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "test": "mocha 'js/tests' && truffle test", "test-eth": "truffle test", "test-js": "mocha 'js/tests'", + "test-ts": "mocha js/src/ts/tests/**/merkle.test.ts", "ganache": "ganache-cli", "lint-eth": "solhint 'eth/contracts/**/*.sol'", "prettier": "prettier --write 'eth/contracts/**/*.sol' 'eth/tests/**/*.js' 'js/src/**/*.js' 'js/tests/**/*.js'", @@ -35,13 +36,22 @@ "sha3": "^2.1.3" }, "devDependencies": { + "@types/chai": "^4.2.18", + "@types/mocha": "^8.2.2", + "@types/node": "^15.6.0", "chai": "^4.2.0", "ganache-cli": "^6.12.0", "mocha": "^8.2.0", "prettier": "^2.1.2", "prettier-plugin-solidity": "^1.0.0-alpha.59", "solhint": "^3.2.2", - "truffle": "^5.1.49" + "truffle": "^5.1.49", + "ts-node": "^9.1.1", + "typescript": "^4.2.4" + }, + "mocha":{ + "require": "ts-node/register", + "grep": "" }, "engines": { "node": ">=14.0.0"