UNPKG

@mavrykdynamics/taquito-michel-codec

Version:

Michelson parser/validator/formatter

1,271 lines (1,270 loc) 42 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.decodePublicKeyBytes = exports.decodePublicKeyHashBytes = exports.decodeAddressBytes = exports.unpackDataBytes = exports.unpackData = exports.packDataBytes = exports.packData = void 0; const michelson_validator_1 = require("./michelson-validator"); const utils_1 = require("./utils"); // The order is important! // The position represent the encoding value. const primitives = [ 'parameter', 'storage', 'code', 'False', 'Elt', 'Left', 'None', 'Pair', 'Right', 'Some', 'True', 'Unit', 'PACK', 'UNPACK', 'BLAKE2B', 'SHA256', 'SHA512', 'ABS', 'ADD', 'AMOUNT', 'AND', 'BALANCE', 'CAR', 'CDR', 'CHECK_SIGNATURE', 'COMPARE', 'CONCAT', 'CONS', 'CREATE_ACCOUNT', 'CREATE_CONTRACT', 'IMPLICIT_ACCOUNT', 'DIP', 'DROP', 'DUP', 'EDIV', 'EMPTY_MAP', 'EMPTY_SET', 'EQ', 'EXEC', 'FAILWITH', 'GE', 'GET', 'GT', 'HASH_KEY', 'IF', 'IF_CONS', 'IF_LEFT', 'IF_NONE', 'INT', 'LAMBDA', 'LE', 'LEFT', 'LOOP', 'LSL', 'LSR', 'LT', 'MAP', 'MEM', 'MUL', 'NEG', 'NEQ', 'NIL', 'NONE', 'NOT', 'NOW', 'OR', 'PAIR', 'PUSH', 'RIGHT', 'SIZE', 'SOME', 'SOURCE', 'SENDER', 'SELF', 'STEPS_TO_QUOTA', 'SUB', 'SWAP', 'TRANSFER_TOKENS', 'SET_DELEGATE', 'UNIT', 'UPDATE', 'XOR', 'ITER', 'LOOP_LEFT', 'ADDRESS', 'CONTRACT', 'ISNAT', 'CAST', 'RENAME', 'bool', 'contract', 'int', 'key', 'key_hash', 'lambda', 'list', 'map', 'big_map', 'nat', 'option', 'or', 'pair', 'set', 'signature', 'string', 'bytes', 'mumav', 'timestamp', 'unit', 'operation', 'address', 'SLICE', 'DIG', 'DUG', 'EMPTY_BIG_MAP', 'APPLY', 'chain_id', 'CHAIN_ID', 'LEVEL', 'SELF_ADDRESS', 'never', 'NEVER', 'UNPAIR', 'VOTING_POWER', 'TOTAL_VOTING_POWER', 'KECCAK', 'SHA3', 'PAIRING_CHECK', 'bls12_381_g1', 'bls12_381_g2', 'bls12_381_fr', 'sapling_state', 'sapling_transaction_deprecated', 'SAPLING_EMPTY_STATE', 'SAPLING_VERIFY_UPDATE', 'ticket', 'TICKET_DEPRECATED', 'READ_TICKET', 'SPLIT_TICKET', 'JOIN_TICKETS', 'GET_AND_UPDATE', 'chest', 'chest_key', 'OPEN_CHEST', 'VIEW', 'view', 'constant', 'SUB_MUMAV', 'tx_rollup_l2_address', 'MIN_BLOCK_TIME', 'sapling_transaction', 'EMIT', 'Lambda_rec', 'LAMBDA_REC', 'TICKET', 'BYTES', 'NAT', 'Ticket', ]; const primTags = Object.assign({}, ...primitives.map((v, i) => ({ [v]: i }))); var Tag; (function (Tag) { Tag[Tag["Int"] = 0] = "Int"; Tag[Tag["String"] = 1] = "String"; Tag[Tag["Sequence"] = 2] = "Sequence"; Tag[Tag["Prim0"] = 3] = "Prim0"; Tag[Tag["Prim0Annot"] = 4] = "Prim0Annot"; Tag[Tag["Prim1"] = 5] = "Prim1"; Tag[Tag["Prim1Annot"] = 6] = "Prim1Annot"; Tag[Tag["Prim2"] = 7] = "Prim2"; Tag[Tag["Prim2Annot"] = 8] = "Prim2Annot"; Tag[Tag["Prim"] = 9] = "Prim"; Tag[Tag["Bytes"] = 10] = "Bytes"; })(Tag || (Tag = {})); class Writer { constructor() { this.buffer = []; } get length() { return this.buffer.length; } writeBytes(val) { this.buffer.push(...val.map((v) => v & 0xff)); } writeUint8(val) { const v = val | 0; this.buffer.push(v & 0xff); } writeUint16(val) { const v = val | 0; this.buffer.push((v >> 8) & 0xff); this.buffer.push(v & 0xff); } writeUint32(val) { const v = val | 0; this.buffer.push((v >> 24) & 0xff); this.buffer.push((v >> 16) & 0xff); this.buffer.push((v >> 8) & 0xff); this.buffer.push(v & 0xff); } writeInt8(val) { this.writeUint8(val); } writeInt16(val) { this.writeUint16(val); } writeInt32(val) { this.writeUint32(val); } } const boundsErr = new Error('bounds out of range'); class Reader { constructor(buffer, idx = 0, cap = buffer.length) { this.buffer = buffer; this.idx = idx; this.cap = cap; } /** Remaining length */ get length() { return this.cap - this.idx; } readBytes(len) { if (this.cap - this.idx < len) { throw boundsErr; } const ret = this.buffer.slice(this.idx, this.idx + len); this.idx += len; return ret; } reader(len) { if (this.cap - this.idx < len) { throw boundsErr; } const ret = new Reader(this.buffer, this.idx, this.idx + len); this.idx += len; return ret; } copy() { return new Reader(this.buffer, this.idx, this.cap); } readUint8() { if (this.cap - this.idx < 1) { throw boundsErr; } return this.buffer[this.idx++] >>> 0; } readUint16() { if (this.cap - this.idx < 2) { throw boundsErr; } const x0 = this.buffer[this.idx++]; const x1 = this.buffer[this.idx++]; return ((x0 << 8) | x1) >>> 0; } readUint32() { if (this.cap - this.idx < 4) { throw boundsErr; } const x0 = this.buffer[this.idx++]; const x1 = this.buffer[this.idx++]; const x2 = this.buffer[this.idx++]; const x3 = this.buffer[this.idx++]; return ((x0 << 24) | (x1 << 16) | (x2 << 8) | x3) >>> 0; } readInt8() { if (this.cap - this.idx < 1) { throw boundsErr; } const x = this.buffer[this.idx++]; return (x << 24) >> 24; } readInt16() { if (this.cap - this.idx < 2) { throw boundsErr; } const x0 = this.buffer[this.idx++]; const x1 = this.buffer[this.idx++]; return (((x0 << 8) | x1) << 16) >> 16; } readInt32() { if (this.cap - this.idx < 4) { throw boundsErr; } const x0 = this.buffer[this.idx++]; const x1 = this.buffer[this.idx++]; const x2 = this.buffer[this.idx++]; const x3 = this.buffer[this.idx++]; return (x0 << 24) | (x1 << 16) | (x2 << 8) | x3; } } var ContractID; (function (ContractID) { ContractID[ContractID["Implicit"] = 0] = "Implicit"; ContractID[ContractID["Originated"] = 1] = "Originated"; })(ContractID || (ContractID = {})); var PublicKeyHashID; (function (PublicKeyHashID) { PublicKeyHashID[PublicKeyHashID["ED25519"] = 0] = "ED25519"; PublicKeyHashID[PublicKeyHashID["SECP256K1"] = 1] = "SECP256K1"; PublicKeyHashID[PublicKeyHashID["P256"] = 2] = "P256"; })(PublicKeyHashID || (PublicKeyHashID = {})); function readPublicKeyHash(rd) { let type; const tag = rd.readUint8(); switch (tag) { case PublicKeyHashID.ED25519: type = 'ED25519PublicKeyHash'; break; case PublicKeyHashID.SECP256K1: type = 'SECP256K1PublicKeyHash'; break; case PublicKeyHashID.P256: type = 'P256PublicKeyHash'; break; default: throw new Error(`unknown public key hash tag: ${tag}`); } return { type, hash: rd.readBytes(20) }; } function readAddress(rd) { let address; const tag = rd.readUint8(); switch (tag) { case ContractID.Implicit: address = readPublicKeyHash(rd); break; case ContractID.Originated: address = { type: 'ContractHash', hash: rd.readBytes(20), }; rd.readBytes(1); break; default: throw new Error(`unknown address tag: ${tag}`); } if (rd.length !== 0) { // entry point const dec = new TextDecoder(); address.entryPoint = dec.decode(new Uint8Array(rd.readBytes(rd.length))); } return address; } function writePublicKeyHash(a, w) { let tag; switch (a.type) { case 'ED25519PublicKeyHash': tag = PublicKeyHashID.ED25519; break; case 'SECP256K1PublicKeyHash': tag = PublicKeyHashID.SECP256K1; break; case 'P256PublicKeyHash': tag = PublicKeyHashID.P256; break; default: throw new Error(`unexpected address type: ${a.type}`); } w.writeUint8(tag); w.writeBytes(Array.from(a.hash)); } function writeAddress(a, w) { if (a.type === 'ContractHash') { w.writeUint8(ContractID.Originated); w.writeBytes(Array.from(a.hash)); w.writeUint8(0); } else { w.writeUint8(ContractID.Implicit); writePublicKeyHash(a, w); } if (a.entryPoint !== undefined && a.entryPoint !== '' && a.entryPoint !== 'default') { const enc = new TextEncoder(); const bytes = enc.encode(a.entryPoint); w.writeBytes(Array.from(bytes)); } } var PublicKeyID; (function (PublicKeyID) { PublicKeyID[PublicKeyID["ED25519"] = 0] = "ED25519"; PublicKeyID[PublicKeyID["SECP256K1"] = 1] = "SECP256K1"; PublicKeyID[PublicKeyID["P256"] = 2] = "P256"; })(PublicKeyID || (PublicKeyID = {})); function readPublicKey(rd) { let ln; let type; const tag = rd.readUint8(); switch (tag) { case PublicKeyID.ED25519: type = 'ED25519PublicKey'; ln = 32; break; case PublicKeyID.SECP256K1: type = 'SECP256K1PublicKey'; ln = 33; break; case PublicKeyID.P256: type = 'P256PublicKey'; ln = 33; break; default: throw new Error(`unknown public key tag: ${tag}`); } return { type, publicKey: rd.readBytes(ln) }; } function writePublicKey(pk, w) { let tag; switch (pk.type) { case 'ED25519PublicKey': tag = PublicKeyID.ED25519; break; case 'SECP256K1PublicKey': tag = PublicKeyID.SECP256K1; break; case 'P256PublicKey': tag = PublicKeyID.P256; break; default: throw new Error(`unexpected public key type: ${pk.type}`); } w.writeUint8(tag); w.writeBytes(Array.from(pk.publicKey)); } function writeExpr(expr, wr, tf) { var _a, _b; const [e, args] = tf(expr); if (Array.isArray(e)) { const w = new Writer(); for (const v of e) { const a = args.next(); if (a.done) { throw new Error('REPORT ME: iterator is done'); } writeExpr(v, w, a.value); } wr.writeUint8(Tag.Sequence); wr.writeUint32(w.length); wr.writeBytes(w.buffer); return; } if ('string' in e) { const enc = new TextEncoder(); const bytes = enc.encode(e.string); wr.writeUint8(Tag.String); wr.writeUint32(bytes.length); wr.writeBytes(Array.from(bytes)); return; } if ('int' in e) { wr.writeUint8(Tag.Int); let val = BigInt(e.int); const sign = val < 0; if (sign) { val = -val; } let i = 0; do { const bits = i === 0 ? BigInt(6) : BigInt(7); let byte = val & ((BigInt(1) << bits) - BigInt(1)); val >>= bits; if (val) { byte |= BigInt(0x80); } if (i === 0 && sign) { byte |= BigInt(0x40); } wr.writeUint8(Number(byte)); i++; } while (val); return; } if ('bytes' in e) { const bytes = (0, utils_1.parseHex)(e.bytes); wr.writeUint8(Tag.Bytes); wr.writeUint32(bytes.length); wr.writeBytes(bytes); return; } const prim = primTags[e.prim]; if (prim === undefined) { throw new TypeError(`Can't encode primary: ${e.prim}`); } const tag = (((_a = e.args) === null || _a === void 0 ? void 0 : _a.length) || 0) < 3 ? Tag.Prim0 + (((_b = e.args) === null || _b === void 0 ? void 0 : _b.length) || 0) * 2 + (e.annots === undefined || e.annots.length === 0 ? 0 : 1) : Tag.Prim; wr.writeUint8(tag); wr.writeUint8(prim); if (e.args !== undefined) { if (e.args.length < 3) { for (const v of e.args) { const a = args.next(); if (a.done) { throw new Error('REPORT ME: iterator is done'); } writeExpr(v, wr, a.value); } } else { const w = new Writer(); for (const v of e.args) { const a = args.next(); if (a.done) { throw new Error('REPORT ME: iterator is done'); } writeExpr(v, w, a.value); } wr.writeUint32(w.length); wr.writeBytes(w.buffer); } } if (e.annots !== undefined && e.annots.length !== 0) { const enc = new TextEncoder(); const bytes = enc.encode(e.annots.join(' ')); wr.writeUint32(bytes.length); wr.writeBytes(Array.from(bytes)); } else if (e.args !== undefined && e.args.length >= 3) { wr.writeUint32(0); } } function readExpr(rd, tf) { function* passThrough() { while (true) { yield readPassThrough; } } const [args, tr] = tf; const tag = rd.readUint8(); switch (tag) { case Tag.Int: { const buf = []; let byte; do { byte = rd.readInt8(); buf.push(byte); } while ((byte & 0x80) !== 0); let val = BigInt(0); let sign = false; for (let i = buf.length - 1; i >= 0; i--) { const bits = i === 0 ? BigInt(6) : BigInt(7); const byte = BigInt(buf[i]); val <<= bits; val |= byte & ((BigInt(1) << bits) - BigInt(1)); if (i === 0) { sign = !!(byte & BigInt(0x40)); } } if (sign) { val = -val; } return tr({ int: String(val) }); } case Tag.String: { const length = rd.readUint32(); const bytes = rd.readBytes(length); const dec = new TextDecoder(); return tr({ string: dec.decode(new Uint8Array(bytes)) }); } case Tag.Bytes: { const length = rd.readUint32(); const bytes = rd.readBytes(length); const hex = (0, utils_1.hexBytes)(Array.from(bytes)); return tr({ bytes: hex }); } case Tag.Sequence: { const length = rd.readUint32(); let res = []; let savedrd = rd.copy(); // make two passes let it = passThrough(); for (let n = 0; n < 2; n++) { const r = savedrd.reader(length); res = []; while (r.length > 0) { const a = it.next(); if (a.done) { throw new Error('REPORT ME: iterator is done'); } res.push(readExpr(r, a.value)); } // make a second pass with injected side effects it = args(res); savedrd = rd; } return tr(res); } default: { if (tag > 9) { throw new Error(`Unknown tag: ${tag}`); } const p = rd.readUint8(); if (p >= primitives.length) { throw new Error(`Unknown primitive tag: ${p}`); } const prim = primitives[p]; const argn = (tag - 3) >> 1; let res = { prim }; // make two passes let it = passThrough(); let savedrd = rd.copy(); for (let n = 0; n < 2; n++) { res = { prim }; if (argn < 3) { for (let i = 0; i < argn; i++) { const a = it.next(); if (a.done) { throw new Error('REPORT ME: iterator is done'); } res.args = res.args || []; res.args.push(readExpr(savedrd, a.value)); } } else { res.args = res.args || []; const length = savedrd.readUint32(); const r = savedrd.reader(length); while (r.length > 0) { const a = it.next(); if (a.done) { throw new Error('REPORT ME: iterator is done'); } res.args.push(readExpr(r, a.value)); } } // make a second pass with injected side effects it = args(res); savedrd = rd; } if (((tag - 3) & 1) === 1 || argn === 3) { // read annotations const length = rd.readUint32(); if (length !== 0) { const bytes = rd.readBytes(length); const dec = new TextDecoder(); res.annots = dec.decode(new Uint8Array(bytes)).split(' '); } } return tr(res); } } } const isOrData = (e) => 'prim' in e && (e.prim === 'Left' || e.prim === 'Right'); const isOptionData = (e) => 'prim' in e && (e.prim === 'Some' || e.prim === 'None'); const getWriteTransformFunc = (t) => { if ((0, utils_1.isPairType)(t)) { return (d) => { if (!(0, utils_1.isPairData)(d)) { throw new utils_1.MichelsonTypeError(t, `pair expected: ${JSON.stringify(d)}`, d); } (0, michelson_validator_1.assertDataListIfAny)(d); // combs aren't used in pack format const tc = (0, utils_1.unpackComb)('pair', t); const dc = (0, utils_1.unpackComb)('Pair', d); return [ dc, (function* () { for (const a of tc.args) { yield getWriteTransformFunc(a); } })(), ]; }; } switch (t.prim) { case 'or': return (d) => { if (!isOrData(d)) { throw new utils_1.MichelsonTypeError(t, `or expected: ${JSON.stringify(d)}`, d); } return [ d, (function* () { yield getWriteTransformFunc(t.args[d.prim === 'Left' ? 0 : 1]); })(), ]; }; case 'option': return (d) => { if (!isOptionData(d)) { throw new utils_1.MichelsonTypeError(t, `option expected: ${JSON.stringify(d)}`, d); } return [ d, (function* () { const dd = d; // TODO: refactor and remove ts-ignore // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore if (dd.prim === 'Some') { yield getWriteTransformFunc(t.args[0]); } })(), ]; }; case 'list': case 'set': return (d) => { if (!Array.isArray(d)) { throw new utils_1.MichelsonTypeError(t, `${t.prim} expected: ${JSON.stringify(d)}`, d); } return [ d, (function* () { for (const _v of d) { yield getWriteTransformFunc(t.args[0]); } })(), ]; }; case 'map': return (d) => { if (!Array.isArray(d)) { throw new utils_1.MichelsonTypeError(t, `map expected: ${JSON.stringify(d)}`, d); } return [ d, (function* () { for (const _elt of d) { yield (elt) => { if (!('prim' in elt) || elt.prim !== 'Elt') { throw new utils_1.MichelsonTypeError(t, `map element expected: ${JSON.stringify(elt)}`, elt); } return [ elt, (function* () { for (const a of t.args) { yield getWriteTransformFunc(a); } })(), ]; }; } })(), ]; }; case 'chain_id': return (d) => { if (!('bytes' in d) && !('string' in d)) { throw new utils_1.MichelsonTypeError(t, `chain id expected: ${JSON.stringify(d)}`, d); } let bytes; if ('string' in d) { const id = (0, utils_1.checkDecodeMavrykID)(d.string, 'ChainID'); if (id === null) { throw new utils_1.MichelsonTypeError(t, `chain id base58 expected: ${d.string}`, d); } bytes = { bytes: (0, utils_1.hexBytes)(id[1]) }; } else { bytes = d; } return [bytes, [][Symbol.iterator]()]; }; case 'signature': return (d) => { if (!('bytes' in d) && !('string' in d)) { throw new utils_1.MichelsonTypeError(t, `signature expected: ${JSON.stringify(d)}`, d); } let bytes; if ('string' in d) { const sig = (0, utils_1.checkDecodeMavrykID)(d.string, 'ED25519Signature', 'SECP256K1Signature', 'P256Signature', 'GenericSignature'); if (sig === null) { throw new utils_1.MichelsonTypeError(t, `signature base58 expected: ${d.string}`, d); } bytes = { bytes: (0, utils_1.hexBytes)(sig[1]) }; } else { bytes = d; } return [bytes, [][Symbol.iterator]()]; }; case 'key_hash': return (d) => { if (!('bytes' in d) && !('string' in d)) { throw new utils_1.MichelsonTypeError(t, `key hash expected: ${JSON.stringify(d)}`, d); } let bytes; if ('string' in d) { const pkh = (0, utils_1.checkDecodeMavrykID)(d.string, 'ED25519PublicKeyHash', 'SECP256K1PublicKeyHash', 'P256PublicKeyHash'); if (pkh === null) { throw new utils_1.MichelsonTypeError(t, `key hash base58 expected: ${d.string}`, d); } const w = new Writer(); writePublicKeyHash({ type: pkh[0], hash: pkh[1] }, w); bytes = { bytes: (0, utils_1.hexBytes)(w.buffer) }; } else { bytes = d; } return [bytes, [][Symbol.iterator]()]; }; case 'key': return (d) => { if (!('bytes' in d) && !('string' in d)) { throw new utils_1.MichelsonTypeError(t, `public key expected: ${JSON.stringify(d)}`, d); } let bytes; if ('string' in d) { const key = (0, utils_1.checkDecodeMavrykID)(d.string, 'ED25519PublicKey', 'SECP256K1PublicKey', 'P256PublicKey'); if (key === null) { throw new utils_1.MichelsonTypeError(t, `public key base58 expected: ${d.string}`, d); } const w = new Writer(); writePublicKey({ type: key[0], publicKey: key[1] }, w); bytes = { bytes: (0, utils_1.hexBytes)(w.buffer) }; } else { bytes = d; } return [bytes, [][Symbol.iterator]()]; }; case 'address': return (d) => { if (!('bytes' in d) && !('string' in d)) { throw new utils_1.MichelsonTypeError(t, `address expected: ${JSON.stringify(d)}`, d); } let bytes; if ('string' in d) { const s = d.string.split('%'); const address = (0, utils_1.checkDecodeMavrykID)(s[0], 'ED25519PublicKeyHash', 'SECP256K1PublicKeyHash', 'P256PublicKeyHash', 'ContractHash'); if (address === null) { throw new utils_1.MichelsonTypeError(t, `address base58 expected: ${d.string}`, d); } const w = new Writer(); writeAddress({ type: address[0], hash: address[1], entryPoint: s.length > 1 ? s[1] : undefined }, w); bytes = { bytes: (0, utils_1.hexBytes)(w.buffer) }; } else { bytes = d; } return [bytes, [][Symbol.iterator]()]; }; case 'timestamp': return (d) => { if (!('string' in d) && !('int' in d)) { throw new utils_1.MichelsonTypeError(t, `timestamp expected: ${JSON.stringify(d)}`, d); } let int; if ('string' in d) { const p = (0, utils_1.parseDate)(d); if (p === null) { throw new utils_1.MichelsonTypeError(t, `can't parse date: ${d.string}`, d); } int = { int: String(Math.floor(p.getTime() / 1000)) }; } else { int = d; } return [int, [][Symbol.iterator]()]; }; default: return writePassThrough; } }; const isPushInstruction = (e) => 'prim' in e && e.prim === 'PUSH'; const writePassThrough = (e) => { if (isPushInstruction(e)) { (0, michelson_validator_1.assertMichelsonInstruction)(e); // capture inlined type definition return [ e, (function* () { yield writePassThrough; yield getWriteTransformFunc(e.args[0]); })(), ]; } return [ e, (function* () { while (true) { yield writePassThrough; } })(), ]; }; /** * Serializes any value of packable type to its optimized binary representation * identical to the one used by PACK and UNPACK Michelson instructions. * Without a type definition (not recommended) the data will be encoded as a binary form of a generic Michelson expression. * Type definition allows some types like `timestamp` and `address` and other base58 representable types to be encoded to * corresponding optimized binary forms borrowed from the Mavryk protocol * * ```typescript * const data: MichelsonData = { * string: "KT1RvkwF4F7pz1gCoxkyZrG1RkrxQy3gmFTv%foo" * }; * * const typ: MichelsonType = { * prim: "address" * }; * * const packed = packData(data, typ); * * // 050a0000001901be41ee922ddd2cf33201e49d32da0afec571dce300666f6f * ``` * * Without a type definition the base58 encoded address will be treated as a string * ```typescript * const data: MichelsonData = { * string: "KT1RvkwF4F7pz1gCoxkyZrG1RkrxQy3gmFTv%foo" * }; * * const packed = packData(data); * * // 0501000000284b543152766b7746344637707a3167436f786b795a724731526b7278517933676d46547625666f6f * ``` * @param d Data object * @param t Optional type definition * @returns Binary representation as numeric array */ function packData(d, t) { const w = new Writer(); w.writeUint8(5); writeExpr(d, w, t !== undefined ? getWriteTransformFunc(t) : writePassThrough); return w.buffer; } exports.packData = packData; /** * Serializes any value of packable type to its optimized binary representation * identical to the one used by PACK and UNPACK Michelson instructions. * Same as {@link packData} but returns a `bytes` Michelson data literal instead of an array * * ```typescript * const data: MichelsonData = { * string: "2019-09-26T10:59:51Z" * }; * * const typ: MichelsonType = { * prim: "timestamp" * }; * * const packed = packDataBytes(data, typ); * * // { bytes: "0500a7e8e4d80b" } * ``` * @param d Data object * @param t Optional type definition * @returns Binary representation as a bytes literal */ function packDataBytes(d, t) { return { bytes: (0, utils_1.hexBytes)(packData(d, t)) }; } exports.packDataBytes = packDataBytes; const getReadTransformFuncs = (t) => { if ((0, utils_1.isPairType)(t)) { return [ (d) => { if (!(0, utils_1.isPairData)(d)) { throw new utils_1.MichelsonTypeError(t, `pair expected: ${JSON.stringify(d)}`, d); } const tc = (0, utils_1.unpackComb)('pair', t); return (function* () { for (const a of tc.args) { yield getReadTransformFuncs(a); } })(); }, (d) => d, ]; } switch (t.prim) { case 'or': return [ (d) => { if (!isOrData(d)) { throw new utils_1.MichelsonTypeError(t, `or expected: ${JSON.stringify(d)}`, d); } return (function* () { yield getReadTransformFuncs(t.args[d.prim === 'Left' ? 0 : 1]); })(); }, (d) => d, ]; case 'option': return [ (d) => { if (!isOptionData(d)) { throw new utils_1.MichelsonTypeError(t, `option expected: ${JSON.stringify(d)}`, d); } return (function* () { // TODO: refactor and remove ts-ignore // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore if (d.prim === 'Some') { yield getReadTransformFuncs(t.args[0]); } })(); }, (d) => d, ]; case 'list': case 'set': return [ (d) => { if (!Array.isArray(d)) { throw new utils_1.MichelsonTypeError(t, `${t.prim} expected: ${JSON.stringify(d)}`, d); } return (function* () { while (true) { yield getReadTransformFuncs(t.args[0]); } })(); }, (d) => d, ]; case 'map': return [ (d) => { if (!Array.isArray(d)) { throw new utils_1.MichelsonTypeError(t, `map expected: ${JSON.stringify(d)}`, d); } return (function* () { while (true) { yield [ (elt) => { if (!('prim' in elt) || elt.prim !== 'Elt') { throw new utils_1.MichelsonTypeError(t, `map element expected: ${JSON.stringify(elt)}`, elt); } return (function* () { for (const a of t.args) { yield getReadTransformFuncs(a); } })(); }, (elt) => elt, ]; } })(); }, (d) => d, ]; case 'chain_id': return [ () => [][Symbol.iterator](), (d) => { if (!('bytes' in d) && !('string' in d)) { throw new utils_1.MichelsonTypeError(t, `chain id expected: ${JSON.stringify(d)}`, d); } if ('string' in d) { return d; } const bytes = (0, utils_1.parseBytes)(d.bytes); if (bytes === null) { throw new utils_1.MichelsonTypeError(t, `can't parse bytes: ${d.bytes}`, d); } return { string: (0, utils_1.encodeMavrykID)('ChainID', bytes) }; }, ]; case 'signature': return [ () => [][Symbol.iterator](), (d) => { if (!('bytes' in d) && !('string' in d)) { throw new utils_1.MichelsonTypeError(t, `signature expected: ${JSON.stringify(d)}`, d); } if ('string' in d) { return d; } const bytes = (0, utils_1.parseBytes)(d.bytes); if (bytes === null) { throw new utils_1.MichelsonTypeError(t, `can't parse bytes: ${d.bytes}`, d); } return { string: (0, utils_1.encodeMavrykID)('GenericSignature', bytes) }; }, ]; case 'key_hash': return [ () => [][Symbol.iterator](), (d) => { if (!('bytes' in d) && !('string' in d)) { throw new utils_1.MichelsonTypeError(t, `key hash expected: ${JSON.stringify(d)}`, d); } if ('string' in d) { return d; } const bytes = (0, utils_1.parseBytes)(d.bytes); if (bytes === null) { throw new utils_1.MichelsonTypeError(t, `can't parse bytes: ${d.bytes}`, d); } const rd = new Reader(new Uint8Array(bytes)); const addr = readPublicKeyHash(rd); return { string: (0, utils_1.encodeMavrykID)(addr.type, addr.hash) + (addr.entryPoint ? '%' + addr.entryPoint : ''), }; }, ]; case 'key': return [ () => [][Symbol.iterator](), (d) => { if (!('bytes' in d) && !('string' in d)) { throw new utils_1.MichelsonTypeError(t, `public key expected: ${JSON.stringify(d)}`, d); } if ('string' in d) { return d; } const bytes = (0, utils_1.parseBytes)(d.bytes); if (bytes === null) { throw new utils_1.MichelsonTypeError(t, `can't parse bytes: ${d.bytes}`, d); } const rd = new Reader(new Uint8Array(bytes)); const pk = readPublicKey(rd); return { string: (0, utils_1.encodeMavrykID)(pk.type, pk.publicKey) }; }, ]; case 'address': return [ () => [][Symbol.iterator](), (d) => { if (!('bytes' in d) && !('string' in d)) { throw new utils_1.MichelsonTypeError(t, `address expected: ${JSON.stringify(d)}`, d); } if ('string' in d) { return d; } const bytes = (0, utils_1.parseBytes)(d.bytes); if (bytes === null) { throw new utils_1.MichelsonTypeError(t, `can't parse bytes: ${d.bytes}`, d); } const rd = new Reader(new Uint8Array(bytes)); const addr = readAddress(rd); return { string: (0, utils_1.encodeMavrykID)(addr.type, addr.hash) + (addr.entryPoint ? '%' + addr.entryPoint : ''), }; }, ]; case 'timestamp': return [ () => [][Symbol.iterator](), (d) => { if (!('int' in d) && !('string' in d)) { throw new utils_1.MichelsonTypeError(t, `address expected: ${JSON.stringify(d)}`, d); } if ('string' in d) { return d; } const date = new Date(parseInt(d.int, 10) * 1000); return { string: date.toISOString().slice(0, 19) + 'Z' }; }, ]; default: return readPassThrough; } }; const readPassThrough = [ (e) => { if (isPushInstruction(e)) { (0, michelson_validator_1.assertMichelsonInstruction)(e); // capture inlined type definition return (function* () { yield readPassThrough; yield getReadTransformFuncs(e.args[0]); })(); } return (function* () { while (true) { yield readPassThrough; } })(); }, (e) => e, ]; /** * Deserialize a byte array into the corresponding Michelson value. * Without a type definition (not recommended) the binary data will be treated as a binary form of a generic Michelson expression and returned as is. * Type definition allows some types like `timestamp` and `address` and other types usually encoded in optimized binary forms to be transformed * back to their string representations like base58 and ISO timestamps. * * ```typescript * const src = [0x05, 0x00, 0xa7, 0xe8, 0xe4, 0xd8, 0x0b]; * * const typ: MichelsonType = { * prim: "timestamp" * }; * * const data = unpackData(src, typ); * * // { string: "2019-09-26T10:59:51Z" } * ``` * * Same binary data without a type definition * ```typescript * const src = [0x05, 0x00, 0xa7, 0xe8, 0xe4, 0xd8, 0x0b]; * * const data = unpackData(src); * * // { int: "1569495591" } * ``` * @param src Byte array * @param t Optional type definition * @returns Deserialized data */ function unpackData(src, t) { const r = new Reader(src); if (r.readUint8() !== 5) { throw new Error('incorrect packed data magic number'); } const ex = readExpr(r, t !== undefined ? getReadTransformFuncs(t) : readPassThrough); if ((0, michelson_validator_1.assertMichelsonData)(ex)) { return ex; } throw new Error(); // never } exports.unpackData = unpackData; /** * Deserialize a byte array into the corresponding Michelson value. * Same as {@link unpackData} but takes a `bytes` Michelson data literal instead of an array * * ```typescript * const src = { bytes: "0500a7e8e4d80b" }; * * const typ: MichelsonType = { * prim: "timestamp" * }; * * const data = unpackDataBytes(src, typ); * * // { string: "2019-09-26T10:59:51Z" } * ``` * @param src Bytes object * @param t Optional type definition * @returns Deserialized data */ function unpackDataBytes(src, t) { const bytes = (0, utils_1.parseBytes)(src.bytes); if (bytes === null) { throw new Error(`can't parse bytes: "${src.bytes}"`); } return unpackData(bytes, t); } exports.unpackDataBytes = unpackDataBytes; // helper functions also used by validator function decodeAddressBytes(b) { const bytes = (0, utils_1.parseBytes)(b.bytes); if (bytes === null) { throw new Error(`can't parse bytes: "${b.bytes}"`); } const rd = new Reader(new Uint8Array(bytes)); return readAddress(rd); } exports.decodeAddressBytes = decodeAddressBytes; function decodePublicKeyHashBytes(b) { const bytes = (0, utils_1.parseBytes)(b.bytes); if (bytes === null) { throw new Error(`can't parse bytes: "${b.bytes}"`); } const rd = new Reader(new Uint8Array(bytes)); return readPublicKeyHash(rd); } exports.decodePublicKeyHashBytes = decodePublicKeyHashBytes; function decodePublicKeyBytes(b) { const bytes = (0, utils_1.parseBytes)(b.bytes); if (bytes === null) { throw new Error(`can't parse bytes: "${b.bytes}"`); } const rd = new Reader(new Uint8Array(bytes)); return readPublicKey(rd); } exports.decodePublicKeyBytes = decodePublicKeyBytes;