UNPKG

@ton/core

Version:

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

232 lines (201 loc) 6.2 kB
/** * 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. */ import inspectSymbol from 'symbol.inspect'; import { BitString } from "./BitString"; import { CellType } from "./CellType"; import { Slice } from "./Slice"; import { LevelMask } from './cell/LevelMask'; import { resolveExotic } from './cell/resolveExotic'; import { wonderCalculator } from './cell/wonderCalculator'; import { deserializeBoc, serializeBoc } from './cell/serialization'; import { BitReader } from './BitReader'; import { beginCell } from './Builder'; /** * Cell as described in TVM spec */ export class Cell { static readonly EMPTY = new Cell(); /** * Deserialize cells from BOC * @param src source buffer * @returns array of cells */ static fromBoc(src: Buffer) { return deserializeBoc(src); } /** * Helper class that deserializes a single cell from BOC in base64 * @param src source string */ static fromBase64(src: string): Cell { let parsed = Cell.fromBoc(Buffer.from(src, 'base64')); if (parsed.length !== 1) { throw new Error("Deserialized more than one cell"); } return parsed[0]; } // Public properties readonly type: CellType; readonly bits: BitString; readonly refs: Cell[]; readonly mask: LevelMask; // Level and depth information private _hashes: Buffer[] = []; private _depths: number[] = []; constructor(opts?: { exotic?: boolean, bits?: BitString, refs?: Cell[] }) { // Resolve bits let bits = BitString.EMPTY; if (opts && opts.bits) { bits = opts.bits; } // Resolve refs let refs: Cell[] = []; if (opts && opts.refs) { refs = [...opts.refs]; } // Resolve type let hashes: Buffer[]; let depths: number[]; let mask: LevelMask; let type = CellType.Ordinary; if (opts && opts.exotic) { // Resolve exotic cell let resolved = resolveExotic(bits, refs); // Perform wonders let wonders = wonderCalculator(resolved.type, bits, refs); // Copy results mask = wonders.mask; depths = wonders.depths; hashes = wonders.hashes; type = resolved.type; } else { // Check correctness if (refs.length > 4) { throw new Error("Invalid number of references"); } if (bits.length > 1023) { throw new Error(`Bits overflow: ${bits.length} > 1023`); } // Perform wonders let wonders = wonderCalculator(CellType.Ordinary, bits, refs); // Copy results mask = wonders.mask; depths = wonders.depths; hashes = wonders.hashes; type = CellType.Ordinary; } // Set fields this.type = type; this.bits = bits; this.refs = refs; this.mask = mask; this._depths = depths; this._hashes = hashes; Object.freeze(this); Object.freeze(this.refs); Object.freeze(this.bits); Object.freeze(this.mask); Object.freeze(this._depths); Object.freeze(this._hashes); } /** * Check if cell is exotic */ get isExotic() { return this.type !== CellType.Ordinary; } /** * Beging cell parsing * @returns a new slice */ beginParse = (allowExotic: boolean = false) => { if (this.isExotic && !allowExotic) { throw new Error("Exotic cells cannot be parsed"); } return new Slice(new BitReader(this.bits), this.refs); } /** * Get cell hash * @param level level * @returns cell hash */ hash = (level: number = 3): Buffer => { return this._hashes[Math.min(this._hashes.length - 1, level)]; } /** * Get cell depth * @param level level * @returns cell depth */ depth = (level: number = 3): number => { return this._depths[Math.min(this._depths.length - 1, level)]; } /** * Get cell level * @returns cell level */ level = (): number => { return this.mask.level; } /** * Checks cell to be euqal to another cell * @param other other cell * @returns true if cells are equal */ equals = (other: Cell): boolean => { return this.hash().equals(other.hash()); } /** * Serializes cell to BOC * @param opts options */ toBoc(opts?: { idx?: boolean | null | undefined, crc32?: boolean | null | undefined }): Buffer { let idx = (opts && opts.idx !== null && opts.idx !== undefined) ? opts.idx : false; let crc32 = (opts && opts.crc32 !== null && opts.crc32 !== undefined) ? opts.crc32 : true; return serializeBoc(this, { idx, crc32 }); } /** * Format cell to string * @param indent indentation * @returns string representation */ toString(indent?: string): string { let id = indent || ''; let t = 'x'; if (this.isExotic) { if (this.type === CellType.MerkleProof) { t = 'p'; } else if (this.type === CellType.MerkleUpdate) { t = 'u'; } else if (this.type === CellType.PrunedBranch) { t = 'p'; } } let s = id + (this.isExotic ? t : 'x') + '{' + this.bits.toString() + '}'; for (let k in this.refs) { const i = this.refs[k]; s += '\n' + i.toString(id + ' '); } return s; } /** * Covnert cell to slice * @returns slice */ asSlice() { return this.beginParse(); } /** * Convert cell to a builder that has this cell stored * @returns builder */ asBuilder() { return beginCell().storeSlice(this.asSlice()); } [inspectSymbol] = () => this.toString(); }