UNPKG

@taquito/utils

Version:

Encoding, crypto, and utility helpers for Taquito.

604 lines (603 loc) 20.5 kB
"use strict"; /** * @packageDocumentation * @module @taquito/utils */ Object.defineProperty(exports, "__esModule", { value: true }); exports.signaturePrefixes = exports.publicKeyHashPrefixes = exports.publicKeyPrefixes = exports.addressPrefixes = void 0; exports.b58DecodeAndCheckPrefix = b58DecodeAndCheckPrefix; exports.b58DecodePublicKey = b58DecodePublicKey; exports.b58DecodePublicKeyHash = b58DecodePublicKeyHash; exports.b58DecodeBlsAddress = b58DecodeBlsAddress; exports.b58DecodeAddress = b58DecodeAddress; exports.getPkhfromPk = getPkhfromPk; exports.b58Encode = b58Encode; exports.encodeKey = encodeKey; exports.encodeKeyHash = encodeKeyHash; exports.encodeAddress = encodeAddress; exports.encodeBlsAddress = encodeBlsAddress; exports.encodeExpr = encodeExpr; exports.encodeOpHash = encodeOpHash; exports.hex2buf = hex2buf; exports.mergebuf = mergebuf; exports.mic2arr = mic2arr; exports.buf2hex = buf2hex; exports.stringToBytes = stringToBytes; exports.bytesToString = bytesToString; exports.hex2Bytes = hex2Bytes; exports.toHexBuf = toHexBuf; exports.numToHexBuffer = numToHexBuffer; exports.num2PaddedHex = num2PaddedHex; exports.stripHexPrefix = stripHexPrefix; exports.splitAddress = splitAddress; exports.compareArrays = compareArrays; /* * Some code in this file is originally from sotez and eztz * Copyright (c) 2018 Andrew Kishino * Copyright (c) 2017 Stephen Andrews */ const buffer_1 = require("buffer"); const constants_1 = require("./constants"); const blake2_js_1 = require("@noble/hashes/blake2.js"); const bs58check_1 = require("bs58check"); const bignumber_js_1 = require("bignumber.js"); const BigNumber = bignumber_js_1.default; const core_1 = require("@taquito/core"); /** * list of prefixes that can be used to decode an address */ exports.addressPrefixes = [ constants_1.PrefixV2.P256PublicKeyHash, constants_1.PrefixV2.Secp256k1PublicKeyHash, constants_1.PrefixV2.Ed25519PublicKeyHash, constants_1.PrefixV2.BLS12_381PublicKeyHash, constants_1.PrefixV2.ContractHash, constants_1.PrefixV2.SmartRollupHash, // PrefixV2.ZkRollupHash, ]; /** * list of prefixes that can be used to decode a public key */ exports.publicKeyPrefixes = [ constants_1.PrefixV2.P256PublicKey, constants_1.PrefixV2.Secp256k1PublicKey, constants_1.PrefixV2.Ed25519PublicKey, constants_1.PrefixV2.BLS12_381PublicKey, ]; /** * list of prefixes that can be used to decode a public key hash */ exports.publicKeyHashPrefixes = [ constants_1.PrefixV2.P256PublicKeyHash, constants_1.PrefixV2.Secp256k1PublicKeyHash, constants_1.PrefixV2.Ed25519PublicKeyHash, constants_1.PrefixV2.BLS12_381PublicKeyHash, ]; /** * list of prefixes that can be used to decode a signature */ exports.signaturePrefixes = [ constants_1.PrefixV2.P256Signature, constants_1.PrefixV2.Secp256k1Signature, constants_1.PrefixV2.Ed25519Signature, constants_1.PrefixV2.BLS12_381Signature, constants_1.PrefixV2.GenericSignature, ]; function b58DecodeAndCheckPrefix(src, allowed, payloadOnly) { const buf = (() => { try { return bs58check_1.default.decode(src); } catch (err) { if (err instanceof Error) { if (err.message.includes('checksum')) { throw new core_1.ParameterValidationError(core_1.ValidationResult.INVALID_CHECKSUM); } else { throw new core_1.ParameterValidationError(core_1.ValidationResult.INVALID_ENCODING); } } else { throw err; } } })(); let key; for (key in constants_1.PrefixV2) { const p = constants_1.PrefixV2[key]; const pre = constants_1.prefixV2[p]; if (buf.length === pre.length + constants_1.payloadLength[p] && buf.slice(0, pre.length).every((v, i) => v == pre[i])) { if (allowed !== undefined && allowed.indexOf(p) < 0) { throw new core_1.ParameterValidationError(core_1.ValidationResult.PREFIX_NOT_ALLOWED); } if (payloadOnly) { return buf.slice(pre.length); } else { return [buf.slice(pre.length), p]; } } } throw new core_1.ParameterValidationError(core_1.ValidationResult.NO_PREFIX_MATCHED); } function b58DecodePublicKey(value, fmt) { const [data, pre] = b58DecodeAndCheckPrefix(value, exports.publicKeyPrefixes); let tag; switch (pre) { case constants_1.PrefixV2.Ed25519PublicKey: tag = 0; break; case constants_1.PrefixV2.Secp256k1PublicKey: tag = 1; break; case constants_1.PrefixV2.P256PublicKey: tag = 2; break; case constants_1.PrefixV2.BLS12_381PublicKey: tag = 3; break; default: throw new core_1.InvalidKeyError(core_1.ValidationResult.NO_PREFIX_MATCHED); } const buf = new Uint8Array(data.length + 1); buf[0] = tag; buf.set(data, 1); if (fmt !== undefined && fmt === 'array') { return buf; } else { return buf2hex(buf); } } function b58DecodePublicKeyHash(value, fmt) { const [data, pre] = b58DecodeAndCheckPrefix(value, exports.publicKeyHashPrefixes); const buf = new Uint8Array(21); let tag; switch (pre) { case constants_1.PrefixV2.Ed25519PublicKeyHash: tag = 0; break; case constants_1.PrefixV2.Secp256k1PublicKeyHash: tag = 1; break; case constants_1.PrefixV2.P256PublicKeyHash: tag = 2; break; case constants_1.PrefixV2.BLS12_381PublicKeyHash: tag = 3; break; default: throw new core_1.InvalidAddressError(value, core_1.ValidationResult.NO_PREFIX_MATCHED); } buf[0] = tag; buf.set(data, 1); if (fmt !== undefined && fmt === 'array') { return buf; } else { return buf2hex(buf); } } function b58DecodeBlsAddress(value, fmt) { const [buf, pre] = b58DecodeAndCheckPrefix(value); if (pre !== constants_1.PrefixV2.BLS12_381PublicKeyHash) { throw new core_1.InvalidKeyError(core_1.ValidationResult.NO_PREFIX_MATCHED); } if (fmt !== undefined && fmt === 'array') { return buf; } else { return buf2hex(buf); } } function b58DecodeAddress(value, fmt) { const i = value.indexOf('%'); if (i >= 0) { value = value.slice(0, i); } const [data, pre] = b58DecodeAndCheckPrefix(value, exports.addressPrefixes); const buf = new Uint8Array(22); if (pre === constants_1.PrefixV2.ContractHash || pre === constants_1.PrefixV2.SmartRollupHash || pre === constants_1.PrefixV2.ZkRollupHash) { let tag; switch (pre) { case constants_1.PrefixV2.ContractHash: tag = 1; break; case constants_1.PrefixV2.SmartRollupHash: tag = 3; break; case constants_1.PrefixV2.ZkRollupHash: tag = 4; break; } buf[0] = tag; buf.set(data, 1); } else { let tag; switch (pre) { case constants_1.PrefixV2.Ed25519PublicKeyHash: tag = 0; break; case constants_1.PrefixV2.Secp256k1PublicKeyHash: tag = 1; break; case constants_1.PrefixV2.P256PublicKeyHash: tag = 2; break; case constants_1.PrefixV2.BLS12_381PublicKeyHash: tag = 3; break; default: throw new core_1.InvalidAddressError(value, core_1.ValidationResult.NO_PREFIX_MATCHED); } buf[0] = 0; buf[1] = tag; buf.set(data, 2); } if (fmt !== undefined && fmt === 'array') { return buf; } else { return buf2hex(buf); } } /** * Gets Tezos address (PKH) from Public Key * @param publicKey Base58 Public Key * @returns A string of the Tezos address (PKH) that was derived from the given Public Key * @example getPkhfromPk('edpkuNjKKT48xBoT5asPrWdmuM1Yw8D93MwgFgVvtca8jb5pstzaCh') // return 'tz2MVED1t9Jery77Bwm1m5YhUx8Wp5KWWRQe' */ function getPkhfromPk(publicKey) { const [key, pre] = b58DecodeAndCheckPrefix(publicKey); let pkhPre; switch (pre) { case constants_1.PrefixV2.P256PublicKey: pkhPre = constants_1.PrefixV2.P256PublicKeyHash; break; case constants_1.PrefixV2.Secp256k1PublicKey: pkhPre = constants_1.PrefixV2.Secp256k1PublicKeyHash; break; case constants_1.PrefixV2.Ed25519PublicKey: pkhPre = constants_1.PrefixV2.Ed25519PublicKeyHash; break; case constants_1.PrefixV2.BLS12_381PublicKey: pkhPre = constants_1.PrefixV2.BLS12_381PublicKeyHash; break; default: throw new core_1.InvalidPublicKeyError(publicKey, core_1.ValidationResult.NO_PREFIX_MATCHED); } const hashed = (0, blake2_js_1.blake2b)(key, { dkLen: 20 }); return b58Encode(hashed, pkhPre); } /** * Add the prefix to a hex string or Uint8Array and Base58 encode it * @param value Value to Base58 encode * @param pre prefix ID to append to the encoded string * @example b58Encode('e96b9f8b19af9c7ffa0c0480e1977b295850961f', PrefixV2.Ed25519PublicKeyHash) // returns 'tz1gvF4cD2dDtqitL3ZTraggSR1Mju2BKFEM' */ function b58Encode(value, pre) { const data = typeof value === 'string' ? hex2buf(value) : value; const p = constants_1.prefixV2[pre]; const n = new Uint8Array(p.length + data.length); n.set(p); n.set(data, p.length); return bs58check_1.default.encode(buffer_1.Buffer.from(n.buffer, n.byteOffset, n.byteLength)); } /** * Parse binary public key and return Base58 representation * @param value Binary key data * @returns return prefixed public key * @example encodeKey('02033aba7da4a2e7b5dd9f074555c118829aff16213ea1b65859686bd5fcfeaf3616') // return 'p2pk66xmhjiN7LpfrDGFwpxPtJxkLtPjQ6HUxJbKmRbxSR7RMpamDwi' */ function encodeKey(value) { let buf; if (typeof value === 'string') { buf = hex2buf(value); } else { buf = value; } let pre; switch (buf[0]) { case 0: pre = constants_1.PrefixV2.Ed25519PublicKey; break; case 1: pre = constants_1.PrefixV2.Secp256k1PublicKey; break; case 2: pre = constants_1.PrefixV2.P256PublicKey; break; case 3: pre = constants_1.PrefixV2.BLS12_381PublicKey; break; default: throw new Error('invalid address format'); } return b58Encode(buf.slice(1), pre); } /** * Parse binary public key hash and return Base58 representation * @param value Key hash to parse * @returns return prefixed public key hash * @example encodeKeyHash('0001907d6a7e9f084df840d6e67ffa8db5464f87d4d1') // return 'tz2MVED1t9Jery77Bwm1m5YhUx8Wp5KWWRQe' */ function encodeKeyHash(value) { let buf; if (typeof value === 'string') { buf = hex2buf(value); } else { buf = value; } let pre; switch (buf[0]) { case 0: pre = constants_1.PrefixV2.Ed25519PublicKeyHash; break; case 1: pre = constants_1.PrefixV2.Secp256k1PublicKeyHash; break; case 2: pre = constants_1.PrefixV2.P256PublicKeyHash; break; case 3: pre = constants_1.PrefixV2.BLS12_381PublicKeyHash; break; default: throw new Error('invalid address format'); } return b58Encode(buf.slice(1, 21), pre); } /** * Parse binary Contract ID and return Base58 representation * @param value Address to parse (tz1, tz2, tz3, KT1, or sr1). * @example encodeAddress('0000e96b9f8b19af9c7ffa0c0480e1977b295850961f') // return 'tz1gvF4cD2dDtqitL3ZTraggSR1Mju2BKFEM' */ function encodeAddress(value) { let buf; if (typeof value === 'string') { buf = hex2buf(value); } else { buf = value; } switch (buf[0]) { case 0: // implicit return encodeKeyHash(buf.slice(1)); case 1: // contract hash return b58Encode(buf.slice(1, 21), constants_1.PrefixV2.ContractHash); case 3: // smart rollup hash return b58Encode(buf.slice(1, 21), constants_1.PrefixV2.SmartRollupHash); case 4: // zk rollup hash return b58Encode(buf.slice(1, 21), constants_1.PrefixV2.ZkRollupHash); default: throw new Error('invalid address format'); } } /** * Base58 encode an address without predefined prefix * @param value Address to base58 encode (tz4) hex dec * @returns return address * @example encodeBlsAddress('af2dc3c40667abc0e89c0ef40171d22aed08d5eb') // return 'tz4QyWfEiv56CVDATV3DT3CDVhPaMKif2Ce8' */ function encodeBlsAddress(value) { return b58Encode(value, constants_1.PrefixV2.BLS12_381PublicKeyHash); } /** * convert a fragment of Michelson code in hex string to an 'expr' prefix + base58 encoded BLAKE2b hash string * @param value a fragment of Michelson code in hex string * @returns return 'expr' prefix + base58 encoded BLAKE2b hash * @example encodeExpr('050a000000160000b2e19a9e74440d86c59f13dab8a18ff873e889ea') // return 'exprv6UsC1sN3Fk2XfgcJCL8NCerP5rCGy1PRESZAqr7L2JdzX55EN' */ function encodeExpr(value) { const blakeHash = (0, blake2_js_1.blake2b)(hex2buf(value), { dkLen: 32 }); return b58Encode(blakeHash, constants_1.PrefixV2.ScriptExpr); } /** * convert a signed operation in hex string to an 'op' prefix + base58 encoded BLAKE2b hash string * @param value signed operation in hex string * @returns return 'op' prefix + base58 encoded BLAKE2b hash * @example encodeOpHash('0f185d8a30061e8134c162dbb7a6c3ab8f5fdb153363ccd6149b49a33481156a6c00b2e19a9e74440d86c59f13dab8a18ff873e889eaa304ab05da13000001f1585a7384f36e45fb43dc37e8ce172bced3e05700ff0000000002002110c033f3a990c2e46a3d6054ecc2f74072aae7a34b5ac4d9ce9edc11c2410a97695682108951786f05b361da03b97245dc9897e1955e08b5b8d9e153b0bdeb0d') // return 'opapqvVXmebRTCFd2GQFydr4tJj3V5QocQuTmuhbatcHm4Seo2t' */ function encodeOpHash(value) { const blakeHash = (0, blake2_js_1.blake2b)(hex2buf(value), { dkLen: 32 }); return b58Encode(blakeHash, constants_1.PrefixV2.OperationHash); } /** * Convert an hex string to a Uint8Array * @param hex Hex string to convert * @throws {@link ValueConversionError} */ function hex2buf(hex) { hex = hex.startsWith('0x') ? hex.slice(2) : hex; if (hex.length % 2 !== 0) { throw new core_1.InvalidHexStringError(hex, `Expecting even number of characters`); } if (!hex.match(/^([\da-f]{2})*$/gi)) { throw new core_1.InvalidHexStringError(hex, `Only characters 0-9, a-f and A-F are expected. Optionally, it can be prefixed with '0x'`); } const res = new Uint8Array(hex.length / 2); let j = 0; for (let i = 0; i < hex.length; i += 2) { const ss = hex.slice(i, i + 2); const x = parseInt(ss, 16); if (Number.isNaN(x)) { throw new core_1.InvalidHexStringError(hex, `Only characters 0-9, a-f and A-F are expected. Optionally, it can be prefixed with '0x'`); } res[j++] = x; } return res; } /** * Merge 2 buffers together * @param b1 First buffer * @param b2 Second buffer */ function mergebuf(b1, b2) { const r = new Uint8Array(b1.length + b2.length); r.set(b1); r.set(b2, b1.length); return r; } /** * Flatten a michelson json representation to an array * @param s michelson json */ function mic2arr(s) { let ret = []; if (Object.prototype.hasOwnProperty.call(s, 'prim')) { if (s.prim === 'Pair') { ret.push(mic2arr(s.args[0])); ret = ret.concat(mic2arr(s.args[1])); } else if (s.prim === 'Elt') { ret = { key: mic2arr(s.args[0]), val: mic2arr(s.args[1]), }; } else if (s.prim === 'True') { ret = true; } else if (s.prim === 'False') { ret = false; } } else if (Array.isArray(s)) { const sc = s.length; for (let i = 0; i < sc; i++) { const n = mic2arr(s[i]); if (typeof n.key !== 'undefined') { if (Array.isArray(ret)) { ret = { keys: [], vals: [], }; } ret.keys.push(n.key); ret.vals.push(n.val); } else { ret.push(n); } } } else if (Object.prototype.hasOwnProperty.call(s, 'string')) { ret = s.string; } else if (Object.prototype.hasOwnProperty.call(s, 'int')) { ret = parseInt(s.int, 10); } else { ret = s; } return ret; } /** * Convert a Uint8Array to an hex string * @param bytes Uint8Array to convert */ function buf2hex(bytes) { return Array.from(bytes) .map((x) => ((x >> 4) & 0xf).toString(16) + (x & 0xf).toString(16)) .join(''); } /** * Convert a string to a byte string representation * @param str String to convert */ function stringToBytes(str) { return buffer_1.Buffer.from(str, 'utf8').toString('hex'); } /** * Convert byte string representation to string * @param hex byte string to convert */ function bytesToString(hex) { return buffer_1.Buffer.from(hex2buf(hex)).toString('utf8'); } /** * Convert hex string/UintArray/Buffer to bytes * @param hex String value to convert to bytes */ function hex2Bytes(hex) { const hexDigits = stripHexPrefix(hex); if (!hexDigits.match(/^(0x)?([\da-f]{2})*$/gi)) { throw new core_1.InvalidHexStringError(hex, `Expecting even number of characters: 0-9, a-z, A-Z, optionally prefixed with 0x`); } return buffer_1.Buffer.from(hexDigits, 'hex'); } /** * Converts a number or Bignumber to hexadecimal string * @param val The value that will be converted to a hexadecimal string value */ function toHexBuf(val, bitLength = 8) { return buffer_1.Buffer.from(num2PaddedHex(val, bitLength), 'hex'); } function numToHexBuffer(val, bitLength = 8) { return buffer_1.Buffer.from(num2PaddedHex(val, bitLength), 'hex'); } /** * Converts a number or BigNumber to a padded hexadecimal string * @param val The value that will be converted into a padded hexadecimal string value * @param bitLength The length of bits * */ function num2PaddedHex(val, bitLength = 8) { if (new BigNumber(val).isPositive()) { const nibbleLength = Math.ceil(bitLength / 4); const hex = val.toString(16); // check whether nibble (4 bits) length is higher or lower than the current hex string length let targetLength = hex.length >= nibbleLength ? hex.length : nibbleLength; // make sure the hex string target length is even targetLength = targetLength % 2 == 0 ? targetLength : targetLength + 1; return padHexWithZero(hex, targetLength); } else { const twosCompliment = new BigNumber(2) .pow(bitLength) .minus(new BigNumber(val).abs()); return twosCompliment.toString(16); } } function padHexWithZero(hex, targetLength) { const padString = '0'; if (hex.length >= targetLength) { return hex; } else { const padLength = targetLength - hex.length; return padString.repeat(padLength) + hex; } } /** * * Strips the first 2 characters of a hex string (0x) * * @param hex string to strip prefix from */ function stripHexPrefix(hex) { return hex.startsWith('0x') ? hex.slice(2) : hex; } function splitAddress(addr) { const i = addr.indexOf('%'); if (i >= 0) { return [addr.slice(0, i), addr.slice(i)]; } else { return [addr, null]; } } function compareArrays(a, b) { let i = 0; while (i < a.length && i < b.length && a[i] === b[i]) i++; const aa = i < a.length ? a[i] : 0; const bb = i < b.length ? b[i] : 0; return aa < bb ? -1 : aa > bb ? 1 : 0; }