Skip to content

Commit

Permalink
feat: implement ViewDU.batchHashTreeRoot() (#392)
Browse files Browse the repository at this point in the history
* feat: implement ViewDU.batchHashTreeRoot()

* chore: add benchmark

* chore: remove describe.only

* fix: dedup test name

* fix: add jsdoc for ViewDU.commit() api
  • Loading branch information
twoeths authored Sep 4, 2024
1 parent 9ddd92e commit 8dd6600
Show file tree
Hide file tree
Showing 37 changed files with 1,753 additions and 125 deletions.
43 changes: 40 additions & 3 deletions packages/persistent-merkle-tree/test/perf/hasher.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {itBench} from "@dapplion/benchmark";
import {HashObject, uint8ArrayToHashObject} from "../../src/hasher";
import {HashObject, setHasher, uint8ArrayToHashObject} from "../../src/hasher";
import {hasher as asShaHasher} from "../../src/hasher/as-sha256";
import {hasher as nobleHasher} from "../../src/hasher/noble";
import {hasher as hashtreeHasher} from "../../src/hasher/hashtree";
import {buildComparisonTrees} from "../utils/tree";
import { HashComputationLevel, getHashComputations } from "../../src";
import {HashComputationLevel, getHashComputations} from "../../src";

describe("hasher", function () {
this.timeout(0);
Expand Down Expand Up @@ -65,4 +65,41 @@ describe("hasher", function () {
}
});

// TODO - batch: test more methods
describe("hashtree", function () {
itBench({
id: `getHashComputations`,
beforeEach: () => {
const [tree] = buildComparisonTrees(16);
return tree;
},
fn: (tree) => {
const hcByLevel: HashComputationLevel[] = [];
getHashComputations(tree, 0, hcByLevel);
},
});

itBench({
id: `executeHashComputations`,
beforeEach: () => {
const [tree] = buildComparisonTrees(16);
return tree;
},
fn: (tree) => {
const hcByLevel: HashComputationLevel[] = [];
getHashComputations(tree, 0, hcByLevel);
hashtreeHasher.executeHashComputations(hcByLevel);
},
});

itBench({
id: `get root`,
beforeEach: () => {
const [tree] = buildComparisonTrees(16);
setHasher(hashtreeHasher);
return tree;
},
fn: (tree) => {
tree.root;
},
});
});
18 changes: 15 additions & 3 deletions packages/ssz/src/type/arrayBasic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
getNodesAtDepth,
packedNodeRootsToBytes,
packedRootsBytesToNode,
HashComputationLevel,
levelAtIndex,
} from "@chainsafe/persistent-merkle-tree";
import {Type, ValueOf, ByteViews} from "./abstract";
import {BasicType} from "./basic";
Expand Down Expand Up @@ -39,14 +41,24 @@ export function addLengthNode(chunksNode: Node, length: number): Node {
return new BranchNode(chunksNode, LeafNode.fromUint32(length));
}

export function setChunksNode(rootNode: Node, chunksNode: Node, newLength?: number): Node {
export function setChunksNode(
rootNode: Node,
chunksNode: Node,
newLength: number | null,
hcOffset = 0,
hcByLevel: HashComputationLevel[] | null = null
): Node {
const lengthNode =
newLength !== undefined
newLength !== null
? // If newLength is set, create a new node for length
LeafNode.fromUint32(newLength)
: // else re-use existing node
(rootNode.right as LeafNode);
return new BranchNode(chunksNode, lengthNode);
const branchNode = new BranchNode(chunksNode, lengthNode);
if (hcByLevel !== null) {
levelAtIndex(hcByLevel, hcOffset).push(chunksNode, lengthNode, branchNode);
}
return branchNode;
}

export type ArrayProps = {isList: true; limit: number} | {isList: false; length: number};
Expand Down
6 changes: 3 additions & 3 deletions packages/ssz/src/type/bitArray.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {concatGindices, Gindex, Node, toGindex, Tree} from "@chainsafe/persistent-merkle-tree";
import {concatGindices, Gindex, Node, toGindex, Tree, HashComputationLevel} from "@chainsafe/persistent-merkle-tree";
import {fromHexString, toHexString, byteArrayEquals} from "../util/byteArray";
import {splitIntoRootChunks} from "../util/merkleize";
import {CompositeType, LENGTH_GINDEX} from "./composite";
Expand Down Expand Up @@ -29,8 +29,8 @@ export abstract class BitArrayType extends CompositeType<BitArray, BitArrayTreeV
return view.node;
}

commitViewDU(view: BitArrayTreeViewDU): Node {
view.commit();
commitViewDU(view: BitArrayTreeViewDU, hcOffset = 0, hcByLevel: HashComputationLevel[] | null = null): Node {
view.commit(hcOffset, hcByLevel);
return view.node;
}

Expand Down
19 changes: 16 additions & 3 deletions packages/ssz/src/type/byteArray.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import {concatGindices, Gindex, Node, toGindex, Tree} from "@chainsafe/persistent-merkle-tree";
import {
concatGindices,
Gindex,
Node,
toGindex,
Tree,
HashComputationLevel,
getHashComputations,
} from "@chainsafe/persistent-merkle-tree";
import {fromHexString, toHexString, byteArrayEquals} from "../util/byteArray";
import {splitIntoRootChunks} from "../util/merkleize";
import {ByteViews} from "./abstract";
Expand Down Expand Up @@ -37,11 +45,16 @@ export abstract class ByteArrayType extends CompositeType<ByteArray, ByteArray,
return this.commitViewDU(view);
}

commitViewDU(view: ByteArray): Node {
// there is no respective ViewDU for this type
commitViewDU(view: ByteArray, hcOffset = 0, hcByLevel: HashComputationLevel[] | null = null): Node {
const uint8Array = new Uint8Array(this.value_serializedSize(view));
const dataView = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
this.value_serializeToBytes({uint8Array, dataView}, 0, view);
return this.tree_deserializeFromBytes({uint8Array, dataView}, 0, uint8Array.length);
const node = this.tree_deserializeFromBytes({uint8Array, dataView}, 0, uint8Array.length);
if (hcByLevel !== null && node.h0 === null) {
getHashComputations(node, hcOffset, hcByLevel);
}
return node;
}

cacheOfViewDU(): unknown {
Expand Down
3 changes: 2 additions & 1 deletion packages/ssz/src/type/composite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Proof,
ProofType,
Tree,
HashComputationLevel,
} from "@chainsafe/persistent-merkle-tree";
import {byteArrayEquals} from "../util/byteArray";
import {merkleize, symbolCachedPermanentRoot, ValueWithCachedPermanentRoot} from "../util/merkleize";
Expand Down Expand Up @@ -126,7 +127,7 @@ export abstract class CompositeType<V, TV, TVDU> extends Type<V> {
/** INTERNAL METHOD: Given a Tree View, returns a `Node` with all its updated data */
abstract commitView(view: TV): Node;
/** INTERNAL METHOD: Given a Deferred Update Tree View returns a `Node` with all its updated data */
abstract commitViewDU(view: TVDU): Node;
abstract commitViewDU(view: TVDU, hcOffset?: number, hcByLevel?: HashComputationLevel[] | null): Node;
/** INTERNAL METHOD: Return the cache of a Deferred Update Tree View. May return `undefined` if this ViewDU has no cache */
abstract cacheOfViewDU(view: TVDU): unknown;

Expand Down
9 changes: 7 additions & 2 deletions packages/ssz/src/type/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
toGindex,
concatGindices,
getNode,
HashComputationLevel,
} from "@chainsafe/persistent-merkle-tree";
import {maxChunksToDepth} from "../util/merkleize";
import {Require} from "../util/types";
Expand Down Expand Up @@ -162,8 +163,12 @@ export class ContainerType<Fields extends Record<string, Type<unknown>>> extends
return view.node;
}

commitViewDU(view: ContainerTreeViewDUType<Fields>): Node {
view.commit();
commitViewDU(
view: ContainerTreeViewDUType<Fields>,
hcOffset = 0,
hcByLevel: HashComputationLevel[] | null = null
): Node {
view.commit(hcOffset, hcByLevel);
return view.node;
}

Expand Down
25 changes: 20 additions & 5 deletions packages/ssz/src/type/listBasic.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {LeafNode, Node, Tree} from "@chainsafe/persistent-merkle-tree";
import {LeafNode, Node, Tree, HashComputationLevel} from "@chainsafe/persistent-merkle-tree";
import {ValueOf} from "./abstract";
import {BasicType} from "./basic";
import {ByteViews} from "./composite";
Expand Down Expand Up @@ -93,8 +93,12 @@ export class ListBasicType<ElementType extends BasicType<unknown>>
return view.node;
}

commitViewDU(view: ListBasicTreeViewDU<ElementType>): Node {
view.commit();
commitViewDU(
view: ListBasicTreeViewDU<ElementType>,
hcOffset = 0,
hcByLevel: HashComputationLevel[] | null = null
): Node {
view.commit(hcOffset, hcByLevel);
return view.node;
}

Expand Down Expand Up @@ -144,8 +148,19 @@ export class ListBasicType<ElementType extends BasicType<unknown>>
return node.left;
}

tree_setChunksNode(rootNode: Node, chunksNode: Node, newLength?: number): Node {
return setChunksNode(rootNode, chunksNode, newLength);
tree_chunksNodeOffset(): number {
// one more level for length, see setChunksNode below
return 1;
}

tree_setChunksNode(
rootNode: Node,
chunksNode: Node,
newLength: number | null,
hcOffset = 0,
hcByLevel: HashComputationLevel[] | null = null
): Node {
return setChunksNode(rootNode, chunksNode, newLength, hcOffset, hcByLevel);
}

// Merkleization
Expand Down
25 changes: 20 additions & 5 deletions packages/ssz/src/type/listComposite.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Node, Tree} from "@chainsafe/persistent-merkle-tree";
import {Node, Tree, HashComputationLevel} from "@chainsafe/persistent-merkle-tree";
import {
mixInLength,
maxChunksToDepth,
Expand Down Expand Up @@ -97,8 +97,12 @@ export class ListCompositeType<
return view.node;
}

commitViewDU(view: ListCompositeTreeViewDU<ElementType>): Node {
view.commit();
commitViewDU(
view: ListCompositeTreeViewDU<ElementType>,
hcOffset = 0,
hcByLevel: HashComputationLevel[] | null = null
): Node {
view.commit(hcOffset, hcByLevel);
return view.node;
}

Expand Down Expand Up @@ -150,8 +154,19 @@ export class ListCompositeType<
return node.left;
}

tree_setChunksNode(rootNode: Node, chunksNode: Node, newLength?: number): Node {
return setChunksNode(rootNode, chunksNode, newLength);
tree_chunksNodeOffset(): number {
// one more level for length, see setChunksNode below
return 1;
}

tree_setChunksNode(
rootNode: Node,
chunksNode: Node,
newLength: number | null,
hcOffset = 0,
hcByLevel: HashComputationLevel[] | null = null
): Node {
return setChunksNode(rootNode, chunksNode, newLength, hcOffset, hcByLevel);
}

// Merkleization
Expand Down
18 changes: 15 additions & 3 deletions packages/ssz/src/type/optional.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import {concatGindices, Gindex, Node, Tree, zeroNode} from "@chainsafe/persistent-merkle-tree";
import {
concatGindices,
Gindex,
Node,
Tree,
zeroNode,
HashComputationLevel,
getHashComputations,
} from "@chainsafe/persistent-merkle-tree";
import {mixInLength} from "../util/merkleize";
import {Require} from "../util/types";
import {namedClass} from "../util/named";
Expand Down Expand Up @@ -75,8 +83,12 @@ export class OptionalType<ElementType extends Type<unknown>> extends CompositeTy
}

// TODO add an OptionalViewDU
commitViewDU(view: ValueOfType<ElementType>): Node {
return this.value_toTree(view);
commitViewDU(view: ValueOfType<ElementType>, hcOffset = 0, hcByLevel: HashComputationLevel[] | null = null): Node {
const node = this.value_toTree(view);
if (hcByLevel !== null && node.h0 === null) {
getHashComputations(node, hcOffset, hcByLevel);
}
return node;
}

// TODO add an OptionalViewDU
Expand Down
18 changes: 15 additions & 3 deletions packages/ssz/src/type/union.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import {concatGindices, getNode, Gindex, Node, Tree} from "@chainsafe/persistent-merkle-tree";
import {
concatGindices,
getNode,
Gindex,
Node,
Tree,
HashComputationLevel,
getHashComputations,
} from "@chainsafe/persistent-merkle-tree";
import {mixInLength} from "../util/merkleize";
import {Require} from "../util/types";
import {namedClass} from "../util/named";
Expand Down Expand Up @@ -106,8 +114,12 @@ export class UnionType<Types extends Type<unknown>[]> extends CompositeType<
return this.value_toTree(view);
}

commitViewDU(view: ValueOfTypes<Types>): Node {
return this.value_toTree(view);
commitViewDU(view: ValueOfTypes<Types>, hcOffset = 0, hcByLevel: HashComputationLevel[] | null = null): Node {
const node = this.value_toTree(view);
if (hcByLevel !== null && node.h0 === null) {
getHashComputations(node, hcOffset, hcByLevel);
}
return node;
}

value_serializedSize(value: ValueOfTypes<Types>): number {
Expand Down
14 changes: 11 additions & 3 deletions packages/ssz/src/type/vectorBasic.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Node, Tree} from "@chainsafe/persistent-merkle-tree";
import {Node, Tree, HashComputationLevel} from "@chainsafe/persistent-merkle-tree";
import {maxChunksToDepth, splitIntoRootChunks} from "../util/merkleize";
import {Require} from "../util/types";
import {namedClass} from "../util/named";
Expand Down Expand Up @@ -83,8 +83,12 @@ export class VectorBasicType<ElementType extends BasicType<unknown>>
return view.node;
}

commitViewDU(view: ArrayBasicTreeViewDU<ElementType>): Node {
view.commit();
commitViewDU(
view: ArrayBasicTreeViewDU<ElementType>,
hcOffset = 0,
hcByLevel: HashComputationLevel[] | null = null
): Node {
view.commit(hcOffset, hcByLevel);
return view.node;
}

Expand Down Expand Up @@ -132,6 +136,10 @@ export class VectorBasicType<ElementType extends BasicType<unknown>>
return node;
}

tree_chunksNodeOffset(): number {
return 0;
}

tree_setChunksNode(rootNode: Node, chunksNode: Node): Node {
return chunksNode;
}
Expand Down
14 changes: 11 additions & 3 deletions packages/ssz/src/type/vectorComposite.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Node, Tree} from "@chainsafe/persistent-merkle-tree";
import {Node, Tree, HashComputationLevel} from "@chainsafe/persistent-merkle-tree";
import {maxChunksToDepth} from "../util/merkleize";
import {Require} from "../util/types";
import {namedClass} from "../util/named";
Expand Down Expand Up @@ -90,8 +90,12 @@ export class VectorCompositeType<
return view.node;
}

commitViewDU(view: ArrayCompositeTreeViewDU<ElementType>): Node {
view.commit();
commitViewDU(
view: ArrayCompositeTreeViewDU<ElementType>,
hcOffset = 0,
hcByLevel: HashComputationLevel[] | null = null
): Node {
view.commit(hcOffset, hcByLevel);
return view.node;
}

Expand Down Expand Up @@ -139,6 +143,10 @@ export class VectorCompositeType<
return node;
}

tree_chunksNodeOffset(): number {
return 0;
}

tree_setChunksNode(rootNode: Node, chunksNode: Node): Node {
return chunksNode;
}
Expand Down
Loading

0 comments on commit 8dd6600

Please sign in to comment.