UNPKG

@ton/core

Version:

Core TypeScript library that implements low level primitives for TON blockchain.

278 lines (277 loc) 9.21 kB
"use strict"; /** * Copyright (c) Whales Corp. * All Rights Reserved. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.serializeBoc = exports.deserializeBoc = exports.parseBoc = void 0; const BitReader_1 = require("../BitReader"); const BitString_1 = require("../BitString"); const Cell_1 = require("../Cell"); const topologicalSort_1 = require("./utils/topologicalSort"); const bitsForNumber_1 = require("../../utils/bitsForNumber"); const BitBuilder_1 = require("../BitBuilder"); const descriptor_1 = require("./descriptor"); const paddedBits_1 = require("../utils/paddedBits"); const crc32c_1 = require("../../utils/crc32c"); function getHashesCount(levelMask) { return getHashesCountFromMask(levelMask & 7); } function getHashesCountFromMask(mask) { let n = 0; for (let i = 0; i < 3; i++) { n += (mask & 1); mask = mask >> 1; } return n + 1; // 1 repr + up to 3 higher hashes } function readCell(reader, sizeBytes) { // D1 const d1 = reader.loadUint(8); const refsCount = d1 % 8; const exotic = !!(d1 & 8); // D2 const d2 = reader.loadUint(8); const dataBytesize = Math.ceil(d2 / 2); const paddingAdded = !!(d2 % 2); const levelMask = d1 >> 5; const hasHashes = (d1 & 16) != 0; const hash_bytes = 32; const hashesSize = hasHashes ? getHashesCount(levelMask) * hash_bytes : 0; const depthSize = hasHashes ? getHashesCount(levelMask) * 2 : 0; reader.skip(hashesSize * 8); reader.skip(depthSize * 8); // Bits let bits = BitString_1.BitString.EMPTY; if (dataBytesize > 0) { if (paddingAdded) { bits = reader.loadPaddedBits(dataBytesize * 8); } else { bits = reader.loadBits(dataBytesize * 8); } } // Refs let refs = []; for (let i = 0; i < refsCount; i++) { refs.push(reader.loadUint(sizeBytes * 8)); } // Result return { bits, refs, exotic }; } function calcCellSize(cell, sizeBytes) { return 2 /* D1+D2 */ + Math.ceil(cell.bits.length / 8) + cell.refs.length * sizeBytes; } function parseBoc(src) { let reader = new BitReader_1.BitReader(new BitString_1.BitString(src, 0, src.length * 8)); let magic = reader.loadUint(32); if (magic === 0x68ff65f3) { let size = reader.loadUint(8); let offBytes = reader.loadUint(8); let cells = reader.loadUint(size * 8); let roots = reader.loadUint(size * 8); // Must be 1 let absent = reader.loadUint(size * 8); let totalCellSize = reader.loadUint(offBytes * 8); let index = reader.loadBuffer(cells * offBytes); let cellData = reader.loadBuffer(totalCellSize); return { size, offBytes, cells, roots, absent, totalCellSize, index, cellData, root: [0] }; } else if (magic === 0xacc3a728) { let size = reader.loadUint(8); let offBytes = reader.loadUint(8); let cells = reader.loadUint(size * 8); let roots = reader.loadUint(size * 8); // Must be 1 let absent = reader.loadUint(size * 8); let totalCellSize = reader.loadUint(offBytes * 8); let index = reader.loadBuffer(cells * offBytes); let cellData = reader.loadBuffer(totalCellSize); let crc32 = reader.loadBuffer(4); if (!(0, crc32c_1.crc32c)(src.subarray(0, src.length - 4)).equals(crc32)) { throw Error('Invalid CRC32C'); } return { size, offBytes, cells, roots, absent, totalCellSize, index, cellData, root: [0] }; } else if (magic === 0xb5ee9c72) { let hasIdx = reader.loadUint(1); let hasCrc32c = reader.loadUint(1); let hasCacheBits = reader.loadUint(1); let flags = reader.loadUint(2); // Must be 0 let size = reader.loadUint(3); let offBytes = reader.loadUint(8); let cells = reader.loadUint(size * 8); let roots = reader.loadUint(size * 8); let absent = reader.loadUint(size * 8); let totalCellSize = reader.loadUint(offBytes * 8); let root = []; for (let i = 0; i < roots; i++) { root.push(reader.loadUint(size * 8)); } let index = null; if (hasIdx) { index = reader.loadBuffer(cells * offBytes); } let cellData = reader.loadBuffer(totalCellSize); if (hasCrc32c) { let crc32 = reader.loadBuffer(4); if (!(0, crc32c_1.crc32c)(src.subarray(0, src.length - 4)).equals(crc32)) { throw Error('Invalid CRC32C'); } } return { size, offBytes, cells, roots, absent, totalCellSize, index, cellData, root }; } else { throw Error('Invalid magic'); } } exports.parseBoc = parseBoc; function deserializeBoc(src) { // // Parse BOC // let boc = parseBoc(src); let reader = new BitReader_1.BitReader(new BitString_1.BitString(boc.cellData, 0, boc.cellData.length * 8)); // // Load cells // let cells = []; for (let i = 0; i < boc.cells; i++) { let cll = readCell(reader, boc.size); cells.push({ ...cll, result: null }); } // // Build cells // for (let i = cells.length - 1; i >= 0; i--) { if (cells[i].result) { throw Error('Impossible'); } let refs = []; for (let r of cells[i].refs) { if (!cells[r].result) { throw Error('Invalid BOC file'); } refs.push(cells[r].result); } cells[i].result = new Cell_1.Cell({ bits: cells[i].bits, refs, exotic: cells[i].exotic }); } // // Load roots // let roots = []; for (let i = 0; i < boc.root.length; i++) { roots.push(cells[boc.root[i]].result); } // // Return // return roots; } exports.deserializeBoc = deserializeBoc; function writeCellToBuilder(cell, refs, sizeBytes, to) { let d1 = (0, descriptor_1.getRefsDescriptor)(cell.refs, cell.mask.value, cell.type); let d2 = (0, descriptor_1.getBitsDescriptor)(cell.bits); to.writeUint(d1, 8); to.writeUint(d2, 8); to.writeBuffer((0, paddedBits_1.bitsToPaddedBuffer)(cell.bits)); for (let r of refs) { to.writeUint(r, sizeBytes * 8); } } function serializeBoc(root, opts) { // Sort cells let allCells = (0, topologicalSort_1.topologicalSort)(root); // Calculcate parameters let cellsNum = allCells.length; let has_idx = opts.idx; let has_crc32c = opts.crc32; let has_cache_bits = false; let flags = 0; let sizeBytes = Math.max(Math.ceil((0, bitsForNumber_1.bitsForNumber)(cellsNum, 'uint') / 8), 1); let totalCellSize = 0; let index = []; for (let c of allCells) { let sz = calcCellSize(c.cell, sizeBytes); totalCellSize += sz; index.push(totalCellSize); } let offsetBytes = Math.max(Math.ceil((0, bitsForNumber_1.bitsForNumber)(totalCellSize, 'uint') / 8), 1); let totalSize = (4 + // magic 1 + // flags and s_bytes 1 + // offset_bytes 3 * sizeBytes + // cells_num, roots, complete offsetBytes + // full_size 1 * sizeBytes + // root_idx (has_idx ? cellsNum * offsetBytes : 0) + totalCellSize + (has_crc32c ? 4 : 0)) * 8; // Serialize let builder = new BitBuilder_1.BitBuilder(totalSize); builder.writeUint(0xb5ee9c72, 32); // Magic builder.writeBit(has_idx); // Has index builder.writeBit(has_crc32c); // Has crc32c builder.writeBit(has_cache_bits); // Has cache bits builder.writeUint(flags, 2); // Flags builder.writeUint(sizeBytes, 3); // Size bytes builder.writeUint(offsetBytes, 8); // Offset bytes builder.writeUint(cellsNum, sizeBytes * 8); // Cells num builder.writeUint(1, sizeBytes * 8); // Roots num builder.writeUint(0, sizeBytes * 8); // Absent num builder.writeUint(totalCellSize, offsetBytes * 8); // Total cell size builder.writeUint(0, sizeBytes * 8); // Root id == 0 if (has_idx) { // Index for (let i = 0; i < cellsNum; i++) { builder.writeUint(index[i], offsetBytes * 8); } } for (let i = 0; i < cellsNum; i++) { // Cells writeCellToBuilder(allCells[i].cell, allCells[i].refs, sizeBytes, builder); } if (has_crc32c) { let crc32 = (0, crc32c_1.crc32c)(builder.buffer()); // builder.buffer() is fast since it doesn't allocate new memory builder.writeBuffer(crc32); } // Sanity Check let res = builder.buffer(); if (res.length !== totalSize / 8) { throw Error('Internal error'); } return res; } exports.serializeBoc = serializeBoc;