Skip to content

Commit

Permalink
fix: support StableContainerTreeViewDU.batchHashTreeRoot()
Browse files Browse the repository at this point in the history
  • Loading branch information
twoeths committed Sep 20, 2024
1 parent b9d2aee commit ded7143
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 87 deletions.
14 changes: 8 additions & 6 deletions packages/ssz/src/view/stableContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export type NonOptionalFields<Fields extends Record<string, Type<unknown>>> = {
};

/** Expected API of this View's type. This interface allows to break a recursive dependency between types and views */
export type ContainerTypeGeneric<Fields extends Record<string, Type<unknown>>> = CompositeType<
export type StableContainerTypeGeneric<Fields extends Record<string, Type<unknown>>> = CompositeType<
ValueOfFields<Fields>,
ContainerTreeViewType<Fields>,
unknown
Expand Down Expand Up @@ -67,9 +67,9 @@ export type FieldsView<Fields extends Record<string, Type<unknown>>> = {
};

export type ContainerTreeViewType<Fields extends Record<string, Type<unknown>>> = FieldsView<Fields> &
TreeView<ContainerTypeGeneric<Fields>>;
TreeView<StableContainerTypeGeneric<Fields>>;
export type ContainerTreeViewTypeConstructor<Fields extends Record<string, Type<unknown>>> = {
new (type: ContainerTypeGeneric<Fields>, tree: Tree): ContainerTreeViewType<Fields>;
new (type: StableContainerTypeGeneric<Fields>, tree: Tree): ContainerTreeViewType<Fields>;
};

/**
Expand All @@ -87,8 +87,10 @@ export type ContainerTreeViewTypeConstructor<Fields extends Record<string, Type<
* iterate the entire data structure and views
*
*/
class ContainerTreeView<Fields extends Record<string, Type<unknown>>> extends TreeView<ContainerTypeGeneric<Fields>> {
constructor(readonly type: ContainerTypeGeneric<Fields>, readonly tree: Tree) {
class ContainerTreeView<Fields extends Record<string, Type<unknown>>> extends TreeView<
StableContainerTypeGeneric<Fields>
> {
constructor(readonly type: StableContainerTypeGeneric<Fields>, readonly tree: Tree) {
super();
}

Expand All @@ -98,7 +100,7 @@ class ContainerTreeView<Fields extends Record<string, Type<unknown>>> extends Tr
}

export function getContainerTreeViewClass<Fields extends Record<string, Type<unknown>>>(
type: ContainerTypeGeneric<Fields>
type: StableContainerTypeGeneric<Fields>
): ContainerTreeViewTypeConstructor<Fields> {
class CustomContainerTreeView extends ContainerTreeView<Fields> {}

Expand Down
94 changes: 13 additions & 81 deletions packages/ssz/src/viewDU/stableContainer.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {getNodeAtDepth, LeafNode, Node, setNodesAtDepth, zeroNode} from "@chainsafe/persistent-merkle-tree";
import {getNodeAtDepth, LeafNode, Node, zeroNode} from "@chainsafe/persistent-merkle-tree";
import {ByteViews, Type} from "../type/abstract";
import {BasicType, isBasicType} from "../type/basic";
import {CompositeType, isCompositeType, CompositeTypeAny} from "../type/composite";
import {computeSerdesData, ContainerTypeGeneric} from "../view/stableContainer";
import {CompositeType, isCompositeType} from "../type/composite";
import {computeSerdesData, StableContainerTypeGeneric} from "../view/stableContainer";
import {TreeViewDU} from "./abstract";
import {OptionalType} from "../type/optional";
import {BitArray} from "../value/bitArray";
import {BasicContainerTreeViewDU} from "./container";

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

Expand All @@ -30,9 +31,9 @@ export type FieldsViewDU<Fields extends Record<string, Type<unknown>>> = {
};

export type ContainerTreeViewDUType<Fields extends Record<string, Type<unknown>>> = FieldsViewDU<Fields> &
TreeViewDU<ContainerTypeGeneric<Fields>>;
TreeViewDU<StableContainerTypeGeneric<Fields>>;
export type ContainerTreeViewDUTypeConstructor<Fields extends Record<string, Type<unknown>>> = {
new (type: ContainerTypeGeneric<Fields>, node: Node, cache?: unknown): ContainerTreeViewDUType<Fields>;
new (type: StableContainerTypeGeneric<Fields>, node: Node, cache?: unknown): ContainerTreeViewDUType<Fields>;
};

type ContainerTreeViewDUCache = {
Expand All @@ -42,96 +43,27 @@ type ContainerTreeViewDUCache = {
nodesPopulated: boolean;
};

class ContainerTreeViewDU<Fields extends Record<string, Type<unknown>>> extends TreeViewDU<
ContainerTypeGeneric<Fields>
> {
class StableContainerTreeViewDU<Fields extends Record<string, Type<unknown>>> extends BasicContainerTreeViewDU<Fields> {
/** pending active fields bitvector */
protected activeFields: BitArray;
protected nodes: Node[] = [];
protected caches: unknown[];
protected readonly nodesChanged = new Set<number>();
protected readonly viewsChanged = new Map<number, unknown>();
private nodesPopulated: boolean;

constructor(
readonly type: ContainerTypeGeneric<Fields>,
readonly type: StableContainerTypeGeneric<Fields>,
protected _rootNode: Node,
cache?: ContainerTreeViewDUCache
) {
super();
super(type, _rootNode, cache);

if (cache) {
this.activeFields = cache.activeFields;
this.nodes = cache.nodes;
this.caches = cache.caches;
this.nodesPopulated = cache.nodesPopulated;
} else {
this.activeFields = type.tree_getActiveFields(_rootNode);
this.nodes = [];
this.caches = [];
this.nodesPopulated = false;
}
}

get node(): Node {
return this._rootNode;
}

get cache(): ContainerTreeViewDUCache {
return {
activeFields: this.activeFields,
nodes: this.nodes,
caches: this.caches,
nodesPopulated: this.nodesPopulated,
};
}

commit(): void {
if (this.nodesChanged.size === 0 && this.viewsChanged.size === 0) {
return;
}

const nodesChanged: {index: number; node: Node}[] = [];

for (const [index, view] of this.viewsChanged) {
const fieldType = this.type.fieldsEntries[index].fieldType as unknown as CompositeTypeAny;
const node = fieldType.commitViewDU(view);
// Set new node in nodes array to ensure data represented in the tree and fast nodes access is equal
this.nodes[index] = node;
nodesChanged.push({index, node});

// Cache the view's caches to preserve it's data after 'this.viewsChanged.clear()'
const cache = fieldType.cacheOfViewDU(view);
if (cache) this.caches[index] = cache;
}

for (const index of this.nodesChanged) {
nodesChanged.push({index, node: this.nodes[index]});
}

// TODO: Optimize to loop only once, Numerical sort ascending
const nodesChangedSorted = nodesChanged.sort((a, b) => a.index - b.index);
const indexes = nodesChangedSorted.map((entry) => entry.index);
const nodes = nodesChangedSorted.map((entry) => entry.node);

this._rootNode = setNodesAtDepth(this._rootNode, this.type.depth, indexes, nodes);
this._rootNode = this.type.tree_setActiveFields(this._rootNode, this.activeFields);

this.nodesChanged.clear();
this.viewsChanged.clear();
}

protected clearCache(): void {
this.nodes = [];
this.caches = [];
this.nodesPopulated = false;

// Must clear nodesChanged, otherwise a subsequent commit call will break, because it assumes a node is there
this.nodesChanged.clear();

// It's not necessary to clear this.viewsChanged since they have no effect on the cache.
// However preserving _SOME_ caches results in a very unpredictable experience.
this.viewsChanged.clear();
const result = super.cache;
return {...result, activeFields: this.activeFields};
}

/**
Expand Down Expand Up @@ -184,9 +116,9 @@ class ContainerTreeViewDU<Fields extends Record<string, Type<unknown>>> extends
}

export function getContainerTreeViewDUClass<Fields extends Record<string, Type<unknown>>>(
type: ContainerTypeGeneric<Fields>
type: StableContainerTypeGeneric<Fields>
): ContainerTreeViewDUTypeConstructor<Fields> {
class CustomContainerTreeViewDU extends ContainerTreeViewDU<Fields> {}
class CustomContainerTreeViewDU extends StableContainerTreeViewDU<Fields> {}

// Dynamically define prototype methods
for (let index = 0; index < type.fieldsEntries.length; index++) {
Expand Down

0 comments on commit ded7143

Please sign in to comment.