UNPKG

ton3-core

Version:
270 lines 12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CellType = exports.Cell = void 0; const mask_1 = require("./mask"); const slice_1 = require("./slice"); const helpers_1 = require("../utils/helpers"); const numbers_1 = require("../utils/numbers"); const bits_1 = require("../utils/bits"); const hash_1 = require("../utils/hash"); const HASH_BITS = 256; const DEPTH_BITS = 16; var CellType; (function (CellType) { CellType[CellType["Ordinary"] = -1] = "Ordinary"; CellType[CellType["PrunedBranch"] = 1] = "PrunedBranch"; CellType[CellType["LibraryReference"] = 2] = "LibraryReference"; CellType[CellType["MerkleProof"] = 3] = "MerkleProof"; CellType[CellType["MerkleUpdate"] = 4] = "MerkleUpdate"; })(CellType || (CellType = {})); exports.CellType = CellType; const validateOrdinary = (bits, refs) => { if (bits.length > 1023) throw new Error(`Ordinary cell can't has more than 1023 bits, got "${bits.length}"`); if (refs.length > 4) throw new Error(`Ordinary cell can't has more than 4 refs, got "${refs.length}"`); }; const validatePrunedBranch = (bits, refs) => { const minSize = 8 + 8 + (1 * (HASH_BITS + DEPTH_BITS)); if (bits.length < minSize) { throw new Error(`Pruned Branch cell can't has less than (8 + 8 + 256 + 16) bits, got "${bits.length}"`); } if (refs.length !== 0) { throw new Error(`Pruned Branch cell can't has refs, got "${refs.length}"`); } const type = (0, numbers_1.bitsToIntUint)(bits.slice(0, 8), { type: 'int' }); if (type !== CellType.PrunedBranch) { throw new Error(`Pruned Branch cell type must be exactly ${CellType.PrunedBranch}, got "${type}"`); } const mask = new mask_1.Mask((0, numbers_1.bitsToIntUint)(bits.slice(8, 16), { type: 'uint' })); if (mask.level < 1 || mask.level > 3) { throw new Error(`Pruned Branch cell level must be >= 1 and <= 3, got "${mask.level}"`); } const { hashCount } = mask.apply(mask.level - 1); const size = 8 + 8 + (hashCount * (HASH_BITS + DEPTH_BITS)); if (bits.length !== size) { throw new Error(`Pruned Branch cell with level "${mask.level}" must have exactly ${size} bits, got "${bits.length}"`); } }; const validateLibraryReference = (bits, refs) => { const size = 8 + HASH_BITS; if (bits.length !== size) { throw new Error(`Library Reference cell must have exactly (8 + 256) bits, got "${bits.length}"`); } if (refs.length !== 0) { throw new Error(`Library Reference cell can't has refs, got "${refs.length}"`); } const type = (0, numbers_1.bitsToIntUint)(bits.slice(0, 8), { type: 'int' }); if (type !== CellType.LibraryReference) { throw new Error(`Library Reference cell type must be exactly ${CellType.LibraryReference}, got "${type}"`); } }; const validateMerkleProof = (bits, refs) => { const size = 8 + HASH_BITS + DEPTH_BITS; if (bits.length !== size) { throw new Error(`Merkle Proof cell must have exactly (8 + 256 + 16) bits, got "${bits.length}"`); } if (refs.length !== 1) { throw new Error(`Merkle Proof cell must have exactly 1 ref, got "${refs.length}"`); } const type = (0, numbers_1.bitsToIntUint)(bits.slice(0, 8), { type: 'int' }); if (type !== CellType.MerkleProof) { throw new Error(`Merkle Proof cell type must be exactly ${CellType.MerkleProof}, got "${type}"`); } const data = Array.from(bits.slice(8)); const proofHash = (0, helpers_1.bitsToHex)(data.splice(0, HASH_BITS)); const proofDepth = (0, numbers_1.bitsToIntUint)(data.splice(0, DEPTH_BITS), { type: 'uint' }); const refHash = refs[0].hash(0); const refDepth = refs[0].depth(0); if (proofHash !== refHash) { throw new Error(`Merkle Proof cell ref hash must be exactly "${proofHash}", got "${refHash}"`); } if (proofDepth !== refDepth) { throw new Error(`Merkle Proof cell ref depth must be exactly "${proofDepth}", got "${refDepth}"`); } }; const validateMerkleUpdate = (bits, refs) => { const size = 8 + (2 * (HASH_BITS + DEPTH_BITS)); if (bits.length !== size) { throw new Error(`Merkle Update cell must have exactly (8 + (2 * (256 + 16))) bits, got "${bits.length}"`); } if (refs.length !== 2) { throw new Error(`Merkle Update cell must have exactly 2 refs, got "${refs.length}"`); } const type = (0, numbers_1.bitsToIntUint)(bits.slice(0, 8), { type: 'int' }); if (type !== CellType.MerkleUpdate) { throw new Error(`Merkle Update cell type must be exactly ${CellType.MerkleUpdate}, got "${type}"`); } const data = Array.from(bits.slice(8)); const hashes = [data.splice(0, HASH_BITS), data.splice(0, HASH_BITS)].map(el => (0, helpers_1.bitsToHex)(el)); const depths = [data.splice(0, DEPTH_BITS), data.splice(0, DEPTH_BITS)].map(el => (0, numbers_1.bitsToIntUint)(el, { type: 'uint' })); refs.forEach((ref, i) => { const proofHash = hashes[i]; const proofDepth = depths[i]; const refHash = ref.hash(0); const refDepth = ref.depth(0); if (proofHash !== refHash) { throw new Error(`Merkle Update cell ref #${i} hash must be exactly "${proofHash}", got "${refHash}"`); } if (proofDepth !== refDepth) { throw Error(`Merkle Update cell ref #${i} depth must be exactly "${proofDepth}", got "${refDepth}"`); } }); }; const getMapper = (type) => { const map = new Map([ [CellType.Ordinary, { validate: validateOrdinary, mask: (_b, r) => new mask_1.Mask(r.reduce((acc, el) => acc | el.mask.value, 0)) }], [CellType.PrunedBranch, { validate: validatePrunedBranch, mask: (b) => new mask_1.Mask((0, numbers_1.bitsToIntUint)(b.slice(8, 16), { type: 'uint' })) }], [CellType.LibraryReference, { validate: validateLibraryReference, mask: () => new mask_1.Mask(0) }], [CellType.MerkleProof, { validate: validateMerkleProof, mask: (_b, r) => new mask_1.Mask(r[0].mask.value >> 1) }], [CellType.MerkleUpdate, { validate: validateMerkleUpdate, mask: (_b, r) => new mask_1.Mask((r[0].mask.value | r[1].mask.value) >> 1) }] ]); const result = map.get(type); if (result === undefined) { throw new Error('Unknown cell type'); } return result; }; class Cell { constructor(options) { this.hashes = []; this.depths = []; const { bits = [], refs = [], type = CellType.Ordinary } = options || {}; const { validate, mask } = getMapper(type); validate(bits, refs); this._mask = mask(bits, refs); this._type = type; this._bits = bits; this._refs = refs; this.initialize(); } get bits() { return Array.from(this._bits); } get refs() { return Array.from(this._refs); } get mask() { return this._mask; } get type() { return this._type; } get exotic() { return this._type !== CellType.Ordinary; } initialize() { const hasRefs = this._refs.length > 0; const isMerkle = [CellType.MerkleProof, CellType.MerkleUpdate].includes(this.type); const isPrunedBranch = this.type === CellType.PrunedBranch; const hashIndexOffset = isPrunedBranch ? this.mask.hashCount - 1 : 0; for (let levelIndex = 0, hashIndex = 0; levelIndex <= this.mask.level; levelIndex++) { if (!this.mask.isSignificant(levelIndex)) { continue; } if (hashIndex < hashIndexOffset) { hashIndex++; continue; } if ((hashIndex === hashIndexOffset && levelIndex !== 0 && !isPrunedBranch) || (hashIndex !== hashIndexOffset && levelIndex === 0 && isPrunedBranch)) { throw new Error('Can\'t deserialize cell'); } const refLevel = levelIndex + Number(isMerkle); const refsDescriptor = this.getRefsDescriptor(this.mask.apply(levelIndex)); const bitsDescriptor = this.getBitsDescriptor(); const data = hashIndex !== hashIndexOffset ? (0, helpers_1.hexToBits)(this.hashes[hashIndex - hashIndexOffset - 1]) : this.getAugmentedBits(); const { depthRepr, hashRepr, depth } = this._refs.reduce((acc, ref) => { const refDepth = ref.depth(refLevel); const refHash = ref.hash(refLevel); acc.depthRepr = acc.depthRepr.concat(this.getDepthDescriptor(refDepth)); acc.hashRepr = acc.hashRepr.concat((0, helpers_1.hexToBits)(refHash)); acc.depth = Math.max(acc.depth, refDepth); return acc; }, { depthRepr: [], hashRepr: [], depth: 0 }); const representation = [].concat(refsDescriptor, bitsDescriptor, data, depthRepr, hashRepr); if (this._refs.length && depth >= 1024) { throw new Error('Cell depth can\'t be more than 1024'); } const dest = hashIndex - hashIndexOffset; this.depths[dest] = depth + Number(hasRefs); this.hashes[dest] = (0, hash_1.sha256)((0, helpers_1.bitsToBytes)(representation)); hashIndex++; } } getDepthDescriptor(depth) { const descriptor = Uint8Array.from([Math.floor(depth / 256), depth % 256]); return (0, helpers_1.bytesToBits)(descriptor); } getRefsDescriptor(mask) { const value = this._refs.length + (Number(this.exotic) * 8) + ((mask ? mask.value : this.mask.value) * 32); const descriptor = Uint8Array.from([value]); return (0, helpers_1.bytesToBits)(descriptor); } getBitsDescriptor() { const value = Math.ceil(this._bits.length / 8) + Math.floor(this._bits.length / 8); const descriptor = Uint8Array.from([value]); return (0, helpers_1.bytesToBits)(descriptor); } getAugmentedBits() { return (0, bits_1.augment)(this._bits); } hash(level = 3) { if (this.type !== CellType.PrunedBranch) { return this.hashes[this.mask.apply(level).hashIndex]; } const { hashIndex } = this.mask.apply(level); const { hashIndex: thisHashIndex } = this.mask; const skip = 16 + (hashIndex * HASH_BITS); return hashIndex !== thisHashIndex ? (0, helpers_1.bitsToHex)(this._bits.slice(skip, skip + HASH_BITS)) : this.hashes[0]; } depth(level = 3) { if (this.type !== CellType.PrunedBranch) { return this.depths[this.mask.apply(level).hashIndex]; } const { hashIndex } = this.mask.apply(level); const { hashIndex: thisHashIndex } = this.mask; const skip = 16 + (thisHashIndex * HASH_BITS) + (hashIndex * DEPTH_BITS); return hashIndex !== thisHashIndex ? (0, numbers_1.bitsToIntUint)(this._bits.slice(skip, skip + DEPTH_BITS), { type: 'uint' }) : this.depths[0]; } slice() { return slice_1.Slice.parse(this); } print(indent = 1, size = 0) { const bits = Array.from(this._bits); const areDivisible = bits.length % 4 === 0; const augmented = !areDivisible ? (0, bits_1.augment)(bits, 4) : bits; const fiftHex = `${(0, helpers_1.bitsToHex)(augmented).toUpperCase()}${!areDivisible ? '_' : ''}`; const output = [`${' '.repeat(indent * size)}x{${fiftHex}}\n`]; this._refs.forEach(ref => output.push(ref.print(indent, size + 1))); return output.join(''); } eq(cell) { return this.hash() === cell.hash(); } } exports.Cell = Cell; //# sourceMappingURL=cell.js.map