Skip to content

Commit

Permalink
feat: add capability to cache merkle roots for lists (#349)
Browse files Browse the repository at this point in the history
  • Loading branch information
g11tech authored Mar 6, 2024
1 parent 6220d32 commit 14c4457
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 19 deletions.
2 changes: 1 addition & 1 deletion packages/ssz/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ export {BitArray, getUint8ByteToBitBooleanArray} from "./value/bitArray";
// Utils
export {fromHexString, toHexString, byteArrayEquals} from "./util/byteArray";

export {hash64} from "./util/merkleize";
export {hash64, symbolCachedPermanentRoot} from "./util/merkleize";
4 changes: 2 additions & 2 deletions packages/ssz/src/type/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export abstract class ArrayType<ElementType extends Type<unknown>, TV, TVDU> ext
abstract readonly itemsPerChunk: number;
protected abstract readonly defaultLen: number;

constructor(readonly elementType: ElementType) {
super();
constructor(readonly elementType: ElementType, cachePermanentRootStruct?: boolean) {
super(cachePermanentRootStruct);
}

defaultValue(): ValueOf<ElementType>[] {
Expand Down
12 changes: 2 additions & 10 deletions packages/ssz/src/type/composite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
Tree,
} from "@chainsafe/persistent-merkle-tree";
import {byteArrayEquals} from "../util/byteArray";
import {merkleize} from "../util/merkleize";
import {merkleize, symbolCachedPermanentRoot, ValueWithCachedPermanentRoot} from "../util/merkleize";
import {treePostProcessFromProofNode} from "../util/proof/treePostProcessFromProofNode";
import {Type, ByteViews, JsonPath, JsonPathProp} from "./abstract";
export {ByteViews};
Expand Down Expand Up @@ -37,14 +37,6 @@ export type CompositeViewDU<T extends CompositeType<unknown, unknown, unknown>>
/** Any CompositeType without any generic arguments */
export type CompositeTypeAny = CompositeType<unknown, unknown, unknown>;

/** Dedicated property to cache hashTreeRoot of immutable CompositeType values */
const symbolCachedPermanentRoot = Symbol("ssz_cached_permanent_root");

/** Helper type to cast CompositeType values that may have @see symbolCachedPermanentRoot */
type ValueWithCachedPermanentRoot = {
[symbolCachedPermanentRoot]?: Uint8Array;
};

/* eslint-disable @typescript-eslint/member-ordering */

/**
Expand Down Expand Up @@ -73,7 +65,7 @@ export abstract class CompositeType<V, TV, TVDU> extends Type<V> {
*
* WARNING: Must only be used for immutable values. The cached root is never discarded
*/
private readonly cachePermanentRootStruct?: boolean
protected readonly cachePermanentRootStruct?: boolean
) {
super();
}
Expand Down
27 changes: 24 additions & 3 deletions packages/ssz/src/type/listBasic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import {
addLengthNode,
setChunksNode,
} from "./arrayBasic";
import {mixInLength, maxChunksToDepth, splitIntoRootChunks} from "../util/merkleize";
import {
mixInLength,
maxChunksToDepth,
splitIntoRootChunks,
symbolCachedPermanentRoot,
ValueWithCachedPermanentRoot,
} from "../util/merkleize";
import {Require} from "../util/types";
import {namedClass} from "../util/named";
import {ArrayBasicType} from "../view/arrayBasic";
Expand All @@ -22,6 +28,7 @@ import {ArrayType} from "./array";

export interface ListBasicOpts {
typeName?: string;
cachePermanentRootStruct?: boolean;
}

/**
Expand All @@ -48,7 +55,7 @@ export class ListBasicType<ElementType extends BasicType<unknown>>
protected readonly defaultLen = 0;

constructor(readonly elementType: ElementType, readonly limit: number, opts?: ListBasicOpts) {
super(elementType);
super(elementType, opts?.cachePermanentRootStruct);

if (!elementType.isBasic) throw Error("elementType must be basic");
if (limit === 0) throw Error("List limit must be > 0");
Expand Down Expand Up @@ -144,7 +151,21 @@ export class ListBasicType<ElementType extends BasicType<unknown>>
// Merkleization

hashTreeRoot(value: ValueOf<ElementType>[]): Uint8Array {
return mixInLength(super.hashTreeRoot(value), value.length);
// Return cached mutable root if any
if (this.cachePermanentRootStruct) {
const cachedRoot = (value as ValueWithCachedPermanentRoot)[symbolCachedPermanentRoot];
if (cachedRoot) {
return cachedRoot;
}
}

const root = mixInLength(super.hashTreeRoot(value), value.length);

if (this.cachePermanentRootStruct) {
(value as ValueWithCachedPermanentRoot)[symbolCachedPermanentRoot] = root;
}

return root;
}

protected getRoots(value: ValueOf<ElementType>[]): Uint8Array[] {
Expand Down
27 changes: 24 additions & 3 deletions packages/ssz/src/type/listComposite.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import {Node, Tree} from "@chainsafe/persistent-merkle-tree";
import {mixInLength, maxChunksToDepth} from "../util/merkleize";
import {
mixInLength,
maxChunksToDepth,
symbolCachedPermanentRoot,
ValueWithCachedPermanentRoot,
} from "../util/merkleize";
import {Require} from "../util/types";
import {namedClass} from "../util/named";
import {ValueOf, ByteViews} from "./abstract";
Expand All @@ -24,6 +29,7 @@ import {ArrayType} from "./array";

export interface ListCompositeOpts {
typeName?: string;
cachePermanentRootStruct?: boolean;
}

/**
Expand Down Expand Up @@ -53,12 +59,13 @@ export class ListCompositeType<
protected readonly defaultLen = 0;

constructor(readonly elementType: ElementType, readonly limit: number, opts?: ListCompositeOpts) {
super(elementType);
super(elementType, opts?.cachePermanentRootStruct);

if (elementType.isBasic) throw Error("elementType must not be basic");
if (limit === 0) throw Error("List limit must be > 0");

this.typeName = opts?.typeName ?? `List[${elementType.typeName}, ${limit}]`;

this.maxChunkCount = this.limit;
this.chunkDepth = maxChunksToDepth(this.maxChunkCount);
// Depth includes the extra level for the length node
Expand Down Expand Up @@ -150,7 +157,21 @@ export class ListCompositeType<
// Merkleization

hashTreeRoot(value: ValueOf<ElementType>[]): Uint8Array {
return mixInLength(super.hashTreeRoot(value), value.length);
// Return cached mutable root if any
if (this.cachePermanentRootStruct) {
const cachedRoot = (value as ValueWithCachedPermanentRoot)[symbolCachedPermanentRoot];
if (cachedRoot) {
return cachedRoot;
}
}

const root = mixInLength(super.hashTreeRoot(value), value.length);

if (this.cachePermanentRootStruct) {
(value as ValueWithCachedPermanentRoot)[symbolCachedPermanentRoot] = root;
}

return root;
}

protected getRoots(value: ValueOf<ElementType>[]): Uint8Array[] {
Expand Down
8 changes: 8 additions & 0 deletions packages/ssz/src/util/merkleize.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import {hasher} from "@chainsafe/persistent-merkle-tree/lib/hasher/index";
import {zeroHash} from "./zeros";

/** Dedicated property to cache hashTreeRoot of immutable CompositeType values */
export const symbolCachedPermanentRoot = Symbol("ssz_cached_permanent_root");

/** Helper type to cast CompositeType values that may have @see symbolCachedPermanentRoot */
export type ValueWithCachedPermanentRoot = {
[symbolCachedPermanentRoot]?: Uint8Array;
};

export function hash64(bytes32A: Uint8Array, bytes32B: Uint8Array): Uint8Array {
return hasher.digest64(bytes32A, bytes32B);
}
Expand Down

0 comments on commit 14c4457

Please sign in to comment.