UNPKG

@chainsafe/persistent-merkle-tree

Version:

Merkle tree implemented as a persistent datastructure

146 lines 4.64 kB
import { gindexParent, gindexSibling } from "../gindex.js"; // Not currently in use, but simpler implementation useful for testing /** * Compute both the path and branch indices * * Path indices are parent indices upwards toward the root * Branch indices are witnesses required for a merkle proof */ export function computeProofGindices(gindex) { const path = new Set(); const branch = new Set(); let g = gindex; while (g > 1) { path.add(g); branch.add(gindexSibling(g)); g = gindexParent(g); } return { path, branch }; } /** * Compute both the path and branch indices * * Path indices are parent indices upwards toward the root * Branch indices are witnesses required for a merkle proof */ export function computeProofBitstrings(gindex) { const path = new Set(); const branch = new Set(); let g = gindex; while (g.length > 1) { path.add(g); const lastBit = g.at(-1); const parent = g.substring(0, g.length - 1); branch.add(parent + (Number(lastBit) ^ 1)); g = parent; } return { path, branch }; } /** * Sort generalized indices in-order * @param bitLength maximum bit length of generalized indices to sort */ export function sortInOrderBitstrings(gindices, bitLength) { if (!gindices.length) { return []; } return gindices .map((g) => g.padEnd(bitLength)) .sort() .map((g) => g.trim()); } /** * Sort generalized indices in decreasing order */ export function sortDecreasingBitstrings(gindices) { if (!gindices.length) { return []; } return gindices.sort((a, b) => { if (a.length < b.length) return 1; if (b.length < a.length) return -1; let aPos0 = a.indexOf("0"); let bPos0 = b.indexOf("0"); while (true) { if (aPos0 === -1) return -1; if (bPos0 === -1) return 1; if (aPos0 < bPos0) return 1; if (bPos0 < aPos0) return -1; aPos0 = a.indexOf("0", aPos0 + 1); bPos0 = b.indexOf("0", bPos0 + 1); } }); } /** * Filter out parent generalized indices */ export function filterParentBitstrings(gindices) { const sortedBitstrings = gindices.slice().sort((a, b) => a.length - b.length); const filtered = []; outer: for (let i = 0; i < sortedBitstrings.length; i++) { const bsA = sortedBitstrings[i]; for (let j = i + 1; j < sortedBitstrings.length; j++) { const bsB = sortedBitstrings[j]; if (bsB.startsWith(bsA)) { continue outer; } } filtered.push(bsA); } return filtered; } export var SortOrder; (function (SortOrder) { SortOrder[SortOrder["InOrder"] = 0] = "InOrder"; SortOrder[SortOrder["Decreasing"] = 1] = "Decreasing"; SortOrder[SortOrder["Unsorted"] = 2] = "Unsorted"; })(SortOrder || (SortOrder = {})); /** * Return the set of generalized indices required for a multiproof * This may include all leaves and any necessary witnesses * @param gindices leaves to include in proof * @returns all generalized indices required for a multiproof (leaves and witnesses), deduplicated and sorted */ export function computeMultiProofBitstrings(gindices, includeLeaves = true, sortOrder = SortOrder.InOrder) { const leaves = filterParentBitstrings(gindices); // Maybe initialize the proof indices with the leaves const proof = new Set(includeLeaves ? leaves : []); const paths = new Set(); const branches = new Set(); // Collect all path indices and all branch indices let maxBitLength = 1; for (const gindex of leaves) { if (gindex.length > maxBitLength) maxBitLength = gindex.length; const { path, branch } = computeProofBitstrings(gindex); for (const p of path) { paths.add(p); } for (const b of branch) { branches.add(b); } } // Remove all branches that are included in the paths for (const p of paths) { branches.delete(p); } // Add all remaining branches to the leaves for (const b of branches) { proof.add(b); } switch (sortOrder) { case SortOrder.InOrder: return sortInOrderBitstrings(Array.from(proof), maxBitLength); case SortOrder.Decreasing: return sortDecreasingBitstrings(Array.from(proof)); case SortOrder.Unsorted: return Array.from(proof); } } //# sourceMappingURL=util.js.map