merkletreejs
Version:
Construct Merkle Trees and verify proofs
380 lines (379 loc) • 12.7 kB
JavaScript
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;
;