UNPKG

@taquito/michel-codec

Version:

Michelson parser, validator, and formatter for Taquito.

324 lines (323 loc) 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.tezosPrefix = exports.LongInteger = exports.MichelsonTypeError = exports.MichelsonError = void 0; exports.isMichelsonError = isMichelsonError; exports.parseBytes = parseBytes; exports.compareBytes = compareBytes; exports.isDecimal = isDecimal; exports.isNatural = isNatural; exports.unpackAnnotations = unpackAnnotations; exports.checkDecodeTezosID = checkDecodeTezosID; exports.encodeTezosID = encodeTezosID; exports.unpackComb = unpackComb; exports.isPairType = isPairType; exports.isPairData = isPairData; exports.parseDate = parseDate; exports.parseHex = parseHex; exports.hexBytes = hexBytes; const base58_1 = require("./base58"); const errors_1 = require("./errors"); const core_1 = require("@taquito/core"); /** * @category Error * Error that indicates a Michelson failure occurring */ class MichelsonError extends core_1.TaquitoError { /** * @param val Value of a AST node caused the error * @param message An error message */ constructor(val, message) { super(); this.val = val; this.message = message; this.name = 'MichelsonError'; } } exports.MichelsonError = MichelsonError; function isMichelsonError(err) { return err instanceof MichelsonError; } class MichelsonTypeError extends MichelsonError { /** * @param val Value of a type node caused the error * @param data Value of a data node caused the error * @param message An error message */ constructor(val, message, data) { super(val, message); this.val = val; this.message = message; this.name = 'MichelsonTypeError'; if (data !== undefined) { this.data = data; } } } exports.MichelsonTypeError = MichelsonTypeError; // Ad hoc big integer parser class LongInteger { append(c) { let i = 0; while (c !== 0 || i < this.buf.length) { const m = (this.buf[i] || 0) * 10 + c; this.buf[i++] = m % 256; c = Math.floor(m / 256); } } constructor(arg) { this.neg = false; this.buf = []; if (arg === undefined) { return; } if (typeof arg === 'string') { for (let i = 0; i < arg.length; i++) { const c = arg.charCodeAt(i); if (i === 0 && c === 0x2d) { this.neg = true; } else { if (c < 0x30 || c > 0x39) { throw new errors_1.LongIntegerError(`unexpected character in integer constant "${arg[i]}"`); } this.append(c - 0x30); } } } else if (arg < 0) { this.neg = true; this.append(-arg); } else { this.append(arg); } } cmp(arg) { if (this.neg !== arg.neg) { return (arg.neg ? 1 : 0) - (this.neg ? 1 : 0); } else { let ret = 0; if (this.buf.length !== arg.buf.length) { ret = this.buf.length < arg.buf.length ? -1 : 1; } else if (this.buf.length !== 0) { let i = arg.buf.length - 1; while (i >= 0 && this.buf[i] === arg.buf[i]) { i--; } ret = i < 0 ? 0 : this.buf[i] < arg.buf[i] ? -1 : 1; } return !this.neg ? ret : ret === 0 ? 0 : -ret; } } get sign() { return this.buf.length === 0 ? 0 : this.neg ? -1 : 1; } } exports.LongInteger = LongInteger; function parseBytes(s) { const ret = []; for (let i = 0; i < s.length; i += 2) { const x = parseInt(s.slice(i, i + 2), 16); if (Number.isNaN(x)) { return null; } ret.push(x); } return ret; } function compareBytes(a, b) { if (a.length !== b.length) { return a.length < b.length ? -1 : 1; } else if (a.length !== 0) { let i = 0; while (i < a.length && a[i] === b[i]) { i++; } return i === a.length ? 0 : a[i] < b[i] ? -1 : 1; } else { return 0; } } function isDecimal(x) { try { new LongInteger(x); return true; } catch { return false; } } function isNatural(x) { try { return new LongInteger(x).sign >= 0; } catch { return false; } } const annRe = /^(@%|@%%|%@|[@:%]([_0-9a-zA-Z][_0-9a-zA-Z.%@]*)?)$/; function unpackAnnotations(p, opt) { if (Array.isArray(p)) { return {}; } let field; let type; let vars; if (p.annots !== undefined) { for (const v of p.annots) { if (v.length !== 0) { if (!annRe.test(v) || (!opt?.specialVar && (v === '@%' || v === '@%%')) || (!opt?.specialFields && v === '%@')) { throw new MichelsonError(p, `${p.prim}: unexpected annotation: ${v}`); } switch (v[0]) { case '%': if (opt?.emptyFields || v.length > 1) { field = field || []; field.push(v); } break; case ':': if (v.length > 1) { type = type || []; type.push(v); } break; case '@': if (opt?.emptyVar || v.length > 1) { vars = vars || []; vars.push(v); } break; } } } } return { f: field, t: type, v: vars }; } exports.tezosPrefix = { BlockHash: [32, [1, 52]], // B(51) OperationHash: [32, [5, 116]], // o(51) OperationListHash: [32, [133, 233]], // Lo(52) OperationListListHash: [32, [29, 159, 109]], // LLo(53) ProtocolHash: [32, [2, 170]], // P(51) ContextHash: [32, [79, 199]], // Co(52) ED25519PublicKeyHash: [20, [6, 161, 159]], // tz1(36) SECP256K1PublicKeyHash: [20, [6, 161, 161]], // tz2(36) P256PublicKeyHash: [20, [6, 161, 164]], // tz3(36) ContractHash: [20, [2, 90, 121]], // KT1(36) CryptoboxPublicKeyHash: [16, [153, 103]], // id(30) ED25519Seed: [32, [13, 15, 58, 7]], // edsk(54) ED25519PublicKey: [32, [13, 15, 37, 217]], // edpk(54) SECP256K1SecretKey: [32, [17, 162, 224, 201]], // spsk(54) P256SecretKey: [32, [16, 81, 238, 189]], // p2sk(54) ED25519EncryptedSeed: [56, [7, 90, 60, 179, 41]], // edesk(88) SECP256K1EncryptedSecretKey: [56, [9, 237, 241, 174, 150]], // spesk(88) P256EncryptedSecretKey: [56, [9, 48, 57, 115, 171]], // p2esk(88) SECP256K1PublicKey: [33, [3, 254, 226, 86]], // sppk(55) P256PublicKey: [33, [3, 178, 139, 127]], // p2pk(55) SECP256K1Scalar: [33, [38, 248, 136]], // SSp(53) SECP256K1Element: [33, [5, 92, 0]], // GSp(54) ED25519SecretKey: [64, [43, 246, 78, 7]], // edsk(98) ED25519Signature: [64, [9, 245, 205, 134, 18]], // edsig(99) SECP256K1Signature: [64, [13, 115, 101, 19, 63]], // spsig1(99) P256Signature: [64, [54, 240, 44, 52]], // p2sig(98) GenericSignature: [64, [4, 130, 43]], // sig(96) ChainID: [4, [87, 82, 0]], RollupAddress: [20, [1, 128, 120, 31]], }; function checkDecodeTezosID(id, ...types) { const buf = (0, base58_1.decodeBase58Check)(id); for (const t of types) { const [plen, p] = exports.tezosPrefix[t]; if (buf.length === plen + p.length) { let i = 0; while (i < p.length && buf[i] === p[i]) { i++; } if (i === p.length) { return [t, buf.slice(p.length)]; } } } return null; } function encodeTezosID(id, data) { const [plen, p] = exports.tezosPrefix[id]; if (data.length !== plen) { throw new errors_1.TezosIdEncodeError(`Incorrect data length for ${id}: ${data.length}`); } return (0, base58_1.encodeBase58Check)([...p, ...data]); } function unpackComb(id, v) { const vv = v; const args = Array.isArray(vv) ? vv : vv.args; if (args.length === 2) { // it's a way to make a union of two interfaces not an interface with two independent properties of union types const ret = id === 'pair' ? { prim: 'pair', args, } : { prim: 'Pair', args, }; return ret; } return { ...(Array.isArray(vv) ? { prim: id } : vv), args: [ args[0], { prim: id, args: args.slice(1), }, ], }; } function isPairType(t) { return Array.isArray(t) || t.prim === 'pair'; } function isPairData(d) { return Array.isArray(d) || ('prim' in d && d.prim === 'Pair'); } const rfc3339Re = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])[T ]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|[+-]([01][0-9]|2[0-3]):([0-5][0-9]))$/; function parseDate(a) { if ('string' in a) { if (isNatural(a.string)) { return new Date(parseInt(a.string, 10)); } else if (rfc3339Re.test(a.string)) { const x = new Date(a.string); if (!Number.isNaN(x.valueOf)) { return x; } } } else if (isDecimal(a.int)) { return new Date(parseInt(a.int, 10)); } return null; } function parseHex(s) { const res = []; for (let i = 0; i < s.length; i += 2) { const ss = s.slice(i, i + 2); const x = parseInt(ss, 16); if (Number.isNaN(x)) { throw new errors_1.HexParseError(ss); } res.push(x); } return res; } function hexBytes(bytes) { return bytes.map((x) => ((x >> 4) & 0xf).toString(16) + (x & 0xf).toString(16)).join(''); }