@taquito/utils
Version:
Encoding, crypto, and utility helpers for Taquito.
604 lines (603 loc) • 20.5 kB
JavaScript
"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;
}