UNPKG

@ton/core

Version:

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

127 lines (115 loc) 3.75 kB
import { beginCell } from '../boc/Builder'; import { Cell } from '../boc/Cell'; import { Slice } from '../boc/Slice'; import { DictionaryKeyTypes, Dictionary, DictionaryKey } from './Dictionary'; import { readUnaryLength } from './utils/readUnaryLength'; import { convertToMerkleProof } from '../boc/cell/exoticMerkleProof'; function convertToPrunedBranch(c: Cell): Cell { return beginCell() .storeUint(1, 8) .storeUint(1, 8) .storeBuffer(c.hash(0)) .storeUint(c.depth(0), 16) .endCell({ exotic: true }); } function doGenerateMerkleProof( prefix: string, slice: Slice, n: number, keys: string[] ): Cell { // Reading label const originalCell = slice.asCell(); if (keys.length == 0) { // no keys to prove, prune the whole subdict return convertToPrunedBranch(originalCell); } let lb0 = slice.loadBit() ? 1 : 0; let prefixLength = 0; let pp = prefix; if (lb0 === 0) { // Short label detected // Read prefixLength = readUnaryLength(slice); // Read prefix for (let i = 0; i < prefixLength; i++) { pp += slice.loadBit() ? '1' : '0'; } } else { let lb1 = slice.loadBit() ? 1 : 0; if (lb1 === 0) { // Long label detected prefixLength = slice.loadUint(Math.ceil(Math.log2(n + 1))); for (let i = 0; i < prefixLength; i++) { pp += slice.loadBit() ? '1' : '0'; } } else { // Same label detected let bit = slice.loadBit() ? '1' : '0'; prefixLength = slice.loadUint(Math.ceil(Math.log2(n + 1))); for (let i = 0; i < prefixLength; i++) { pp += bit; } } } if (n - prefixLength === 0) { return originalCell; } else { let sl = originalCell.beginParse(); let left = sl.loadRef(); let right = sl.loadRef(); // NOTE: Left and right branches are implicitly contain prefixes '0' and '1' if (!left.isExotic) { const leftKeys = keys.filter((key) => { return pp + '0' === key.slice(0, pp.length + 1); }); left = doGenerateMerkleProof( pp + '0', left.beginParse(), n - prefixLength - 1, leftKeys ); } if (!right.isExotic) { const rightKeys = keys.filter((key) => { return pp + '1' === key.slice(0, pp.length + 1); }); right = doGenerateMerkleProof( pp + '1', right.beginParse(), n - prefixLength - 1, rightKeys ); } return beginCell() .storeSlice(sl) .storeRef(left) .storeRef(right) .endCell(); } } export function generateMerkleProofDirect<K extends DictionaryKeyTypes, V>( dict: Dictionary<K, V>, keys: K[], keyObject: DictionaryKey<K> ): Cell { keys.forEach((key) => { if (!dict.has(key)) { throw new Error(`Trying to generate merkle proof for a missing key "${key}"`) } }) const s = beginCell().storeDictDirect(dict).asSlice(); return doGenerateMerkleProof( '', s, keyObject.bits, keys.map((key) => keyObject.serialize(key).toString(2).padStart(keyObject.bits, '0')) ); } export function generateMerkleProof<K extends DictionaryKeyTypes, V>( dict: Dictionary<K, V>, keys: K[], keyObject: DictionaryKey<K> ): Cell { return convertToMerkleProof(generateMerkleProofDirect(dict, keys, keyObject)); }