UNPKG

@colony/purser-core

Version:

A collection of helpers, utils, validators and normalizers to assist the individual purser modules

282 lines (270 loc) 10.7 kB
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"; import BN from 'bn.js'; import { assertTruth, validatorGenerator, objectToErrorString } from './utils'; import { validators as messages } from './messages'; import { PATH, MATCH, UNDEFINED, SPLITTER } from './defaults'; /** * Validate a derivation path passed in as a string * * @method derivationPathValidator * * @param {string} derivationPath The derivation path to check * * @return {boolean} It only returns true if the derivation path is correct, * otherwise an Error will be thrown and this will not finish execution. */ export var derivationPathValidator = function derivationPathValidator(derivationPath) { var derivationPathMessages = messages.derivationPath; var COIN_MAINNET = PATH.COIN_MAINNET, COIN_TESTNET = PATH.COIN_TESTNET; var deSerializedDerivationPath; var coinType; try { /* * Because assignments get bubbled to the top of the method, we need to wrap * this inside a try/catch block. * * Otherwise, this will fail before we have a change to assert it. */ deSerializedDerivationPath = derivationPath.split(PATH.DELIMITER); coinType = parseInt(deSerializedDerivationPath[1], 10); } catch (caughtError) { throw new Error("".concat(derivationPathMessages.notString, ": ").concat(derivationPath || UNDEFINED)); } /* * We need to assert this in a separate step, otherwise, if the size of the split * chunks is not correct the `match()` method call will fail before the * validator generator sequence will actually start. */ assertTruth({ /* * It should be composed of (at least) four parts * (purpouse, coin, account, change and/or index) */ expression: deSerializedDerivationPath.length === 4, message: ["".concat(derivationPathMessages.notValidParts, ": [")].concat(_toConsumableArray(deSerializedDerivationPath), [']']) }); var validationSequence = [{ /* * It should have the correct Header Key (the letter 'm') */ expression: deSerializedDerivationPath[0].split(SPLITTER)[0].toLowerCase() === PATH.HEADER_KEY, message: ["".concat(derivationPathMessages.notValidHeaderKey, ":"), deSerializedDerivationPath[0] || UNDEFINED] }, { /* * It should have the Ethereum reserved Purpouse (44) */ expression: parseInt(deSerializedDerivationPath[0].split(SPLITTER)[1], 10) === PATH.PURPOSE, message: ["".concat(derivationPathMessages.notValidPurpouse, ":"), deSerializedDerivationPath[0] || UNDEFINED] }, { /* * It should have the correct Coin type */ expression: coinType === COIN_MAINNET || coinType === COIN_TESTNET, message: ["".concat(derivationPathMessages.notValidCoin, ":"), deSerializedDerivationPath[1] || UNDEFINED] }, { /* * It should have the correct Account format (eg: a number) */ expression: !!deSerializedDerivationPath[2].match(MATCH.DIGITS), message: ["".concat(derivationPathMessages.notValidAccount, ":"), deSerializedDerivationPath[2] || UNDEFINED] }, { /* * It should have the correct Change and/or Account Index format (eg: a number) */ expression: deSerializedDerivationPath[3].split(SPLITTER).map(function (value) { return !!value.match(MATCH.DIGITS); }).every(function (truth) { return truth !== false; }), message: ["".concat(derivationPathMessages.notValidChangeIndex, ":"), deSerializedDerivationPath[3] || UNDEFINED] }, { /* * It should have the correct amount of Account Indexed (just one) */ expression: deSerializedDerivationPath[3].split(SPLITTER).length <= 2, message: ["".concat(derivationPathMessages.notValidAccountIndex, ":"), deSerializedDerivationPath[3] || UNDEFINED] }]; return validatorGenerator(validationSequence, "".concat(derivationPathMessages.genericError, ": ").concat(derivationPath || UNDEFINED)); }; /** * Validate an integer passed in to make sure is safe (< 9007199254740991) and positive * * @method safeIntegerValidator * * @param {number} integer The integer to validate * * @return {boolean} It only returns true if the integer is safe and positive, * otherwise an Error will be thrown and this will not finish execution. */ export var safeIntegerValidator = function safeIntegerValidator(integer) { var safeIntegerMessages = messages.safeInteger; var validationSequence = [{ /* * It should be a number primitive */ expression: typeof integer === 'number', message: "".concat(safeIntegerMessages.notNumber, ": ").concat(integer) }, { /* * It should be a positive number * This is a little less trutfull as integers can also be negative */ expression: integer >= 0, message: "".concat(safeIntegerMessages.notPositive, ": ").concat(integer) }, { /* * It should be under the safe integer limit: ± 9007199254740991 * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger */ expression: Number.isSafeInteger(integer), message: "".concat(safeIntegerMessages.notSafe, ": ").concat(integer) }]; return validatorGenerator(validationSequence, "".concat(safeIntegerMessages.genericError, ": ").concat(integer)); }; /** * Validate a Big Number instance object that was passed in * * @method bigNumberValidator * * @param {Object} bigNumber The big number instance to check * * @return {boolean} It only returns true if the object is an instance of Big Number, * otherwise an Error will be thrown and this will not finish execution. */ export var bigNumberValidator = function bigNumberValidator(bigNumber) { var bigNumberMessages = messages.bigNumber; var validationSequence = [{ /* * It should be an instance of the BN Class */ expression: BN.isBN(bigNumber), message: "".concat(bigNumberMessages.notBigNumber, ": ").concat(objectToErrorString(bigNumber)) }]; return validatorGenerator(validationSequence, "".concat(bigNumberMessages.genericError, ": ").concat(objectToErrorString(bigNumber))); }; /** * Validate a BIP32 Ethereum Address * * @TODO Validate the checksum of the address. * * @method addressValidator * * @param {string} address The 'hex' address to check * * @return {boolean} It only returns true if the string is a valid address format, * otherwise an Error will be thrown and this will not finish execution. */ export var addressValidator = function addressValidator(address) { var addressMessages = messages.address; var addressLength = 0; try { /* * Because length checking is bubbled to the top, we need to to wrap this inside * a separate try-catch block, otherwise the whole thing will fail before the * validation sequence will even start. */ addressLength = address.length; } catch (caughtError) { throw new Error("".concat(addressMessages.notStringSequence, ": ").concat(UNDEFINED)); } var validationSequence = [{ /* * It should be a string */ expression: typeof address === 'string', message: "".concat(addressMessages.notStringSequence, ": ").concat(objectToErrorString(address) || UNDEFINED) }, { /* * It should be the correct length. Either 40 or 42 (with prefix) */ expression: addressLength === 40 || addressLength === 42, message: "".concat(addressMessages.notLength, ": ").concat(address || UNDEFINED) }, { /* * It should be in the correct format (hex string of length 40 with or * with out the `0x` prefix) */ expression: !!address.match(MATCH.ADDRESS), message: "".concat(addressMessages.notFormat, ": ").concat(address || UNDEFINED) }]; return validatorGenerator(validationSequence, "".concat(addressMessages.genericError, ": ").concat(address || UNDEFINED)); }; /** * Validate a hex string * * @method hexSequenceValidator * * @param {string} hexSequence The `hex` string to check * * @return {boolean} It only returns true if the string is a valid hex format, * otherwise an Error will be thrown and this will not finish execution. */ export var hexSequenceValidator = function hexSequenceValidator(hexSequence) { var hexSequenceMessages = messages.hexSequence; var validationSequence = [{ /* * It should be a string */ expression: typeof hexSequence === 'string', message: "".concat(hexSequenceMessages.notStringSequence, ": ").concat(objectToErrorString(hexSequence) || UNDEFINED) }, { /* * It should be in the correct format (hex string with or with out the `0x` prefix) */ expression: !!hexSequence.match(MATCH.HEX_STRING), message: "".concat(hexSequenceMessages.notFormat, ": ").concat(hexSequence || UNDEFINED) }]; return validatorGenerator(validationSequence, "".concat(hexSequenceMessages.genericError, ": ").concat(hexSequence || UNDEFINED)); }; /** * Validate a hex string * * @method messageValidator * * @param {string} string The big number instance to check * * @return {boolean} It only returns true if the string is a valid format, * otherwise an Error will be thrown and this will not finish execution. */ export var messageValidator = function messageValidator(string) { /* * Real creative naming there, huh...? */ var messageMessages = messages.message; var validationSequence = [{ /* * It should be a string */ expression: typeof string === 'string', message: "".concat(messageMessages.notString, ": ").concat(objectToErrorString(string) || UNDEFINED) }, { /* * It should be under (or equal to) 1024 Bytes in size */ expression: string.length <= 1024, message: "".concat(messageMessages.tooBig, ": ").concat(string || UNDEFINED) }]; return validatorGenerator(validationSequence, "".concat(messageMessages.genericError, ": ").concat(string || UNDEFINED)); }; /** * Validate a hex string * * @method messageDataValidator * * @param {any} data The messageData to check * * @return {boolean} It only returns true if the data is a valid format, * otherwise an Error will be thrown and this will not finish execution. */ export var messageDataValidator = function messageDataValidator(data) { var messageMessages = messages.message; var validationSequence = [{ /* * It should be a hex string or UInt8Array */ expression: typeof data === 'string' && hexSequenceValidator(data) || data.constructor === Uint8Array, message: "".concat(messageMessages.notString, ": ").concat(objectToErrorString(data) || UNDEFINED) }]; return validatorGenerator(validationSequence, "".concat(messageMessages.genericError, ": ").concat(data || UNDEFINED)); };