UNPKG

@chainsafe/ssz

Version:
119 lines 5.08 kB
import { allocUnsafe } from "@chainsafe/as-sha256"; import { Tree, concatGindices, merkleizeBlocksBytes, packedNodeRootsToBytes, packedRootsBytesToLeafNodes, } from "@chainsafe/persistent-merkle-tree"; import { byteArrayEquals } from "../util/byteArray.js"; import { namedClass } from "../util/named.js"; import { addLengthNode, getChunksNodeFromRootNode, getLengthFromRootNode } from "./arrayBasic.js"; import { ByteArrayType } from "./byteArray.js"; import { PROGRESSIVE_LIST_MAX_SIZE, getNodesAtProgressiveDepth, merkleizeProgressiveBytes, progressiveChunkGindex, progressiveSubtreeFillToContents, } from "./progressive.js"; const CHUNKS_GINDEX = BigInt(2); const LENGTH_GINDEX = BigInt(3); /** * ProgressiveByteList: Immutable alias of ProgressiveList[byte] * - Value: `Uint8Array` * - View: `Uint8Array` * - ViewDU: `Uint8Array` * * ProgressiveByteList is an immutable value represented by a Uint8Array for memory efficiency and performance. * Note: Consumers of this type MUST never mutate the `Uint8Array` representation of a ProgressiveByteList. */ export class ProgressiveByteListType extends ByteArrayType { typeName; depth = 1; chunkDepth = 0; fixedSize = null; minSize = 0; maxSize = PROGRESSIVE_LIST_MAX_SIZE; maxChunkCount = Number.MAX_SAFE_INTEGER; isList = true; mixInLengthBlockBytes = new Uint8Array(64); mixInLengthBuffer = Buffer.from(this.mixInLengthBlockBytes.buffer, this.mixInLengthBlockBytes.byteOffset, this.mixInLengthBlockBytes.byteLength); constructor(opts) { super(); this.typeName = opts?.typeName ?? "ProgressiveByteList"; } static named(opts) { return new (namedClass(ProgressiveByteListType, opts.typeName))(opts); } createFromProof(proof, root) { const rootNode = Tree.createFromProof(proof).rootNode; if (root !== undefined && !byteArrayEquals(rootNode.root, root)) { throw new Error("Proof does not match trusted root"); } return this.getView(new Tree(rootNode)); } value_serializedSize(value) { return value.length; } tree_serializedSize(node) { return getLengthFromRootNode(node); } tree_serializeToBytes(output, offset, node) { const chunksNode = getChunksNodeFromRootNode(node); const byteLen = getLengthFromRootNode(node); const chunkLen = Math.ceil(byteLen / 32); const nodes = getNodesAtProgressiveDepth(chunksNode, chunkLen); packedNodeRootsToBytes(output.dataView, offset, byteLen, nodes); return offset + byteLen; } tree_deserializeFromBytes(data, start, end) { this.assertValidSize(end - start); const nodes = packedRootsBytesToLeafNodes(data.dataView, start, end); return addLengthNode(progressiveSubtreeFillToContents(nodes), end - start); } tree_getByteLen(node) { if (!node) throw new Error("ProgressiveByteListType requires a node to get leaves"); return getLengthFromRootNode(node); } hashTreeRoot(value) { const root = allocUnsafe(32); this.hashTreeRootInto(value, root, 0); return root; } hashTreeRootInto(value, output, offset) { const blockBytes = this.getBlocksBytes(value); const chunkCount = Math.ceil(value.length / 32); merkleizeProgressiveBytes(blockBytes, chunkCount, this.mixInLengthBlockBytes, 0); this.mixInLengthBuffer.writeUIntLE(value.length, 32, 6); merkleizeBlocksBytes(this.mixInLengthBlockBytes, 2, output, offset); } fromJson(json) { if (!Array.isArray(json)) { return super.fromJson(json); } const value = Uint8Array.from(json.map((byte) => { const byteNumber = typeof byte === "bigint" ? Number(byte) : typeof byte === "number" ? byte : typeof byte === "string" ? Number(byte) : Number.NaN; if (!Number.isInteger(byteNumber) || byteNumber < 0 || byteNumber > 255 || String(byteNumber) !== String(byte)) { throw Error(`Invalid byte value ${byte}`); } return byteNumber; })); this.assertValidSize(value.length); return value; } tree_getLeafGindices(rootGindex, rootNode) { const byteLen = this.tree_getByteLen(rootNode); const chunkCount = Math.ceil(byteLen / 32); const gindices = new Array(chunkCount); for (let i = 0; i < chunkCount; i++) { gindices[i] = concatGindices([rootGindex, CHUNKS_GINDEX, progressiveChunkGindex(i)]); } gindices.push(concatGindices([rootGindex, LENGTH_GINDEX])); return gindices; } assertValidSize(size) { if (size > PROGRESSIVE_LIST_MAX_SIZE) { throw Error(`ProgressiveByteList invalid size ${size} max ${PROGRESSIVE_LIST_MAX_SIZE}`); } } } //# sourceMappingURL=progressiveByteList.js.map