UNPKG

o2-client

Version:

Light Client for O2 Blockchain

248 lines (200 loc) 7.16 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _primitive = require('../types/primitive'); var primitive = _interopRequireWildcard(_primitive); var _hexadecimal = require('../types/hexadecimal'); var _generic = require('../types/generic'); var _convert = require('../types/convert'); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var BIT_LENGTH = 256; var ProofPath = function () { /** * Constructs a proof path from a binary string or a byte buffer. * * @param {string | Uint8Array} bits */ function ProofPath(bits) { var bitLength = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : BIT_LENGTH; _classCallCheck(this, ProofPath); if (typeof bits === 'string') { this.key = (0, _convert.binaryStringToUint8Array)(padWithZeros(bits, BIT_LENGTH)); bitLength = bits.length; } else if (bits instanceof Uint8Array && bits.length === BIT_LENGTH / 8) { this.key = bits.slice(0); } else { throw new TypeError('Invalid `bits` parameter'); } this.isTerminal = bitLength === BIT_LENGTH; this.lengthByte = bitLength % BIT_LENGTH; this.hexKey = (0, _convert.uint8ArrayToHexadecimal)(this.key); } _createClass(ProofPath, [{ key: 'bitLength', value: function bitLength() { return this.isTerminal ? BIT_LENGTH : this.lengthByte; } /** * Retrieves a bit at a specific position of this key. * * @param {number} pos * @returns {0 | 1 | void} */ }, { key: 'bit', value: function bit(pos) { pos = +pos; if (pos >= this.bitLength() || pos < 0) { return undefined; } return getBit(this.key, pos); } }, { key: 'commonPrefixLength', value: function commonPrefixLength(other) { var intersectingBits = Math.min(this.bitLength(), other.bitLength()); // First, advance by a full byte while it is possible var pos = void 0; for (pos = 0; pos < intersectingBits >> 3 && this.key[pos >> 3] === other.key[pos >> 3]; pos += 8) {} // Then, check inidividual bits for (; pos < intersectingBits && this.bit(pos) === other.bit(pos); pos++) {} return pos; } /** * Computes a common prefix of this and another byte sequence. * * @param {ProofPath} other * @returns {ProofPath} */ }, { key: 'commonPrefix', value: function commonPrefix(other) { var pos = this.commonPrefixLength(other); return this.truncate(pos); } /** * Checks if the path starts with the other specified path. * * @param {ProofPath} other * @returns {boolean} */ }, { key: 'startsWith', value: function startsWith(other) { return this.commonPrefixLength(other) === other.bitLength(); } /** * Compares this proof path to another. * * @param {ProofPath} other * @returns {-1 | 0 | 1} */ }, { key: 'compare', value: function compare(other) { var _ref = [this.bitLength(), other.bitLength()], thisLen = _ref[0], otherLen = _ref[1]; var intersectingBits = Math.min(thisLen, otherLen); var pos = this.commonPrefixLength(other); if (pos === intersectingBits) { return Math.sign(thisLen - otherLen); } return this.bit(pos) - other.bit(pos); } /** * Truncates this bit sequence to a shorter one by removing some bits from the end. * * @param {number} bits * new length of the sequence * @returns {ProofPath} * truncated bit sequence */ }, { key: 'truncate', value: function truncate(bits) { bits = +bits; if (bits > this.bitLength()) { throw new TypeError('Cannot truncate bit slice to length more than current ' + ('(current: ' + this.bitLength() + ', requested: ' + bits + ')')); } var bytes = new Uint8Array(BIT_LENGTH / 8); for (var i = 0; i < bits >> 3; i++) { bytes[i] = this.key[i]; } for (var bit = 8 * (bits >> 3); bit < bits; bit++) { setBit(bytes, bit, this.bit(bit)); } return new ProofPath(bytes, bits); } /** * Converts this path to its JSON presentation. * * @returns {string} * binary string representing the path */ }, { key: 'toJSON', value: function toJSON() { var bits = (0, _convert.hexadecimalToBinaryString)(this.hexKey); return trimZeros(bits, this.bitLength()); } }, { key: 'toString', value: function toString() { var bits = (0, _convert.hexadecimalToBinaryString)(this.hexKey); bits = this.bitLength() > 8 ? trimZeros(bits, 8) + '...' : trimZeros(bits, this.bitLength()); return 'path(' + bits + ')'; } }]); return ProofPath; }(); /** * Expected length of byte buffers used to create `ProofPath`s. */ exports.default = ProofPath; ProofPath.BYTE_LENGTH = BIT_LENGTH / 8; ProofPath.TYPE = (0, _generic.newType)({ fields: [{ name: 'isTerminal', type: primitive.Bool }, { name: 'hexKey', type: _hexadecimal.Hash }, { name: 'lengthByte', type: primitive.Uint8 }] }); function getBit(buffer, pos) { var byte = Math.floor(pos / 8); var bitPos = pos % 8; return (buffer[byte] & 1 << bitPos) >> bitPos; } /** * Sets a specified bit in the byte buffer. * * @param {Uint8Array} buffer * @param {number} pos 0-based position in the buffer to set * @param {0 | 1} bit */ function setBit(buffer, pos, bit) { var byte = Math.floor(pos / 8); var bitPos = pos % 8; if (bit === 0) { var mask = 255 - (1 << bitPos); buffer[byte] &= mask; } else { var _mask = 1 << bitPos; buffer[byte] |= _mask; } } var ZEROS = function () { var str = '0'; for (var i = 0; i < 8; i++) { str = str + str; }return str; }(); function padWithZeros(str, desiredLength) { return str + ZEROS.substring(0, desiredLength - str.length); } function trimZeros(str, desiredLength) { /* istanbul ignore next: should never be triggered */ if (str.length < desiredLength) { throw new Error('Invariant broken: negative zero trimming requested'); } return str.substring(0, desiredLength); }