UNPKG

merkletreejs

Version:
380 lines (379 loc) 12.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Base = void 0; const buffer_1 = require("buffer"); const crypto_js_1 = __importDefault(require("crypto-js")); class Base { /** * print * @desc Prints out a visual representation of the merkle tree. * @example *```js *tree.print() *``` */ print() { Base.print(this); } /** * bufferIndexOf * @desc Returns the first index of which given buffer is found in array. * @param {Buffer[]} haystack - Array of buffers. * @param {Buffer} needle - Buffer to find. * @return {Number} - Index number * * @example * ```js *const index = tree.bufferIndexOf(haystack, needle) *``` */ bufferIndexOf(array, element, isSorted = false) { if (isSorted) { return this.binarySearch(array, element, buffer_1.Buffer.compare); } const eqChecker = (buffer1, buffer2) => buffer1.equals(buffer2); return this.linearSearch(array, element, eqChecker); } /** * binarySearch * @desc Returns the first index of which given item is found in array using binary search. * @param {Buffer[]} array - Array of items. * @param {Buffer} element - Item to find. * @param {Function} compareFunction * @return {Number} - Index number * * @example * ```js *const index = MerkleTree.binarySearch(array, element, Buffer.compare) *``` */ static binarySearch(array, element, compareFunction) { let start = 0; let end = array.length - 1; // Iterate while start not meets end while (start <= end) { // Find the mid index const mid = Math.floor((start + end) / 2); // Check if the mid value is greater than, equal to, or less than search element. const ordering = compareFunction(array[mid], element); // If element is present at mid, start iterating for searching first appearance. if (ordering === 0) { // Linear reverse iteration until the first matching item index is found. for (let i = mid - 1; i >= 0; i--) { if (compareFunction(array[i], element) === 0) continue; return i + 1; } return 0; } /* Else look in left or right half accordingly */ else if (ordering < 0) { start = mid + 1; } else { end = mid - 1; } } return -1; } /** * binarySearch * @desc Returns the first index of which given item is found in array using binary search. * @param {Buffer[]} array - Array of items. * @param {Buffer} element - Item to find. * @param {Function} compareFunction * @return {Number} - Index number * * @example * ```js *const index = tree.binarySearch(array, element, Buffer.compare) *``` */ binarySearch(array, element, compareFunction) { return Base.binarySearch(array, element, compareFunction); } /** * linearSearch * @desc Returns the first index of which given item is found in array using linear search. * @param {Buffer[]} array - Array of items. * @param {Buffer} element - Item to find. * @param {Function} eqChecker * @return {Number} - Index number * * @example * ```js *const index = MerkleTree.linearSearch(array, element, (a, b) => a === b) *``` */ static linearSearch(array, element, eqChecker) { for (let i = 0; i < array.length; i++) { if (eqChecker(array[i], element)) { return i; } } return -1; } /** * linearSearch * @desc Returns the first index of which given item is found in array using linear search. * @param {Buffer[]} array - Array of items. * @param {Buffer} element - Item to find. * @param {Function} eqChecker * @return {Number} - Index number * * @example * ```js *const index = tree.linearSearch(array, element, (a, b) => a === b) *``` */ linearSearch(array, element, eqChecker) { return Base.linearSearch(array, element, eqChecker); } /** * bufferify * @desc Returns a buffer type for the given value. * @param {String|Number|Object|Buffer|ArrayBuffer} value * @return {Buffer} * * @example * ```js *const buf = MerkleTree.bufferify('0x1234') *``` */ static bufferify(value) { if (!buffer_1.Buffer.isBuffer(value)) { // crypto-js support if (typeof value === 'object' && value.words) { return buffer_1.Buffer.from(value.toString(crypto_js_1.default.enc.Hex), 'hex'); } else if (Base.isHexString(value)) { const hexString = value.replace('0x', ''); const paddedHexString = hexString.length % 2 ? '0' + hexString : hexString; return buffer_1.Buffer.from(paddedHexString, 'hex'); } else if (typeof value === 'string') { return buffer_1.Buffer.from(value); } else if (typeof value === 'bigint') { const hexString = value.toString(16).length % 2 ? '0' + value.toString(16) : value.toString(16); return buffer_1.Buffer.from(hexString, 'hex'); } else if (value instanceof Uint8Array) { return buffer_1.Buffer.from(value.buffer, value.byteOffset, value.byteLength); } else if (typeof value === 'number') { let s = value.toString(); if (s.length % 2) { s = `0${s}`; } return buffer_1.Buffer.from(s, 'hex'); } else if (ArrayBuffer.isView(value)) { return buffer_1.Buffer.from(value.buffer, value.byteOffset, value.byteLength); } } return value; } /** * bufferifyFn * @desc Returns a function that will bufferify the return value. * @param {Function} * @return {Function} * * @example * ```js *const fn = MerkleTree.bufferifyFn((value) => sha256(value)) *``` */ static bufferifyFn(f) { if (typeof f !== 'function') { throw new Error(`bufferifyFn expects a function, received: ${typeof f}`); } return (value) => { const v = f(value); if (buffer_1.Buffer.isBuffer(v)) { return v; } if (Base.isHexString(v)) { const hexString = v.replace('0x', ''); const paddedHexString = hexString.length % 2 ? '0' + hexString : hexString; return buffer_1.Buffer.from(paddedHexString, 'hex'); } if (typeof v === 'string') { return buffer_1.Buffer.from(v); } if (typeof v === 'bigint') { const hexString = v.toString(16).length % 2 ? '0' + v.toString(16) : v.toString(16); return buffer_1.Buffer.from(hexString, 'hex'); } if (ArrayBuffer.isView(v)) { return buffer_1.Buffer.from(v.buffer, v.byteOffset, v.byteLength); } // crypto-js support return buffer_1.Buffer.from(f(crypto_js_1.default.enc.Hex.parse(value.toString('hex'))).toString(crypto_js_1.default.enc.Hex), 'hex'); }; } bigNumberify(value) { return Base.bigNumberify(value); } static bigNumberify(value) { if (typeof value === 'bigint') { return value; } if (typeof value === 'string') { if (value.startsWith('0x') && Base.isHexString(value)) { // Remove '0x' and ensure even-length hex string const hexString = value.replace('0x', ''); const paddedHexString = hexString.length % 2 ? '0' + hexString : (hexString || '0'); return BigInt('0x' + paddedHexString); } return BigInt(value); } if (buffer_1.Buffer.isBuffer(value)) { // Convert buffer to hex string and ensure even-length hex string const hexString = value.toString('hex'); const paddedHexString = hexString.length % 2 ? '0' + hexString : (hexString || '0'); return BigInt('0x' + paddedHexString); } if (value instanceof Uint8Array) { // Convert Uint8Array to hex string and ensure even-length hex string const hexString = buffer_1.Buffer.from(value).toString('hex'); const paddedHexString = hexString.length % 2 ? '0' + hexString : (hexString || '0'); return BigInt('0x' + paddedHexString); } if (typeof value === 'number') { return BigInt(value); } throw new Error('cannot bigNumberify'); } /** * isHexString * @desc Returns true if value is a hex string. * @param {String} value * @return {Boolean} * * @example * ```js *console.log(MerkleTree.isHexString('0x1234')) *``` */ static isHexString(v) { return typeof v === 'string' && /^(0x)?[0-9A-Fa-f]*$/.test(v); } /** * print * @desc Prints out a visual representation of the given merkle tree. * @param {Object} tree - Merkle tree instance. * @return {String} * @example *```js *MerkleTree.print(tree) *``` */ static print(tree) { console.log(tree.toString()); } /** * bufferToHex * @desc Returns a hex string with 0x prefix for given buffer. * @param {Buffer} value * @return {String} * @example *```js *const hexStr = tree.bufferToHex(Buffer.from('A')) *``` */ bufferToHex(value, withPrefix = true) { return Base.bufferToHex(value, withPrefix); } /** * bufferToHex * @desc Returns a hex string with 0x prefix for given buffer. * @param {Buffer} value * @return {String} * @example *```js *const hexStr = MerkleTree.bufferToHex(Buffer.from('A')) *``` */ static bufferToHex(value, withPrefix = true) { return `${withPrefix ? '0x' : ''}${(value || buffer_1.Buffer.alloc(0)).toString('hex')}`; } /** * bufferify * @desc Returns a buffer type for the given value. * @param {String|Number|Object|Buffer} value * @return {Buffer} * * @example * ```js *const buf = tree.bufferify('0x1234') *``` */ bufferify(value) { return Base.bufferify(value); } /** * bufferifyFn * @desc Returns a function that will bufferify the return value. * @param {Function} * @return {Function} * * @example * ```js *const fn = tree.bufferifyFn((value) => sha256(value)) *``` */ bufferifyFn(f) { return Base.bufferifyFn(f); } /** * isHexString * @desc Returns true if value is a hex string. * @param {String} value * @return {Boolean} * * @example * ```js *console.log(MerkleTree.isHexString('0x1234')) *``` */ isHexString(value) { return Base.isHexString(value); } /** * log2 * @desc Returns the log2 of number. * @param {Number} value * @return {Number} */ log2(n) { return n === 1 ? 0 : 1 + this.log2((n / 2) | 0); } /** * zip * @desc Returns true if value is a hex string. * @param {String[]|Number[]|Buffer[]} a - first array * @param {String[]|Number[]|Buffer[]} b - second array * @return {String[][]|Number[][]|Buffer[][]} * * @example * ```js *const zipped = tree.zip(['a', 'b'],['A', 'B']) *console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ] *``` */ zip(a, b) { return a.map((e, i) => [e, b[i]]); } static hexZeroPad(hexStr, length) { return '0x' + hexStr.replace('0x', '').padStart(length, '0'); } bufferArrayIncludes(bufferArray, targetBuffer) { return bufferArray.some(buffer => buffer.equals(targetBuffer !== null && targetBuffer !== void 0 ? targetBuffer : buffer_1.Buffer.alloc(0))); } } exports.Base = Base; exports.default = Base;