Skip to content

Commit

Permalink
feat: new api to upgrade a tree to new type
Browse files Browse the repository at this point in the history
  • Loading branch information
twoeths committed Oct 1, 2024
1 parent 1dc50ef commit 0fb8bbe
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 2 deletions.
5 changes: 5 additions & 0 deletions packages/ssz/src/view/abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,9 @@ export abstract class TreeView<T extends CompositeType<unknown, unknown, unknown
clone(): this {
return this.type.getView(new Tree(this.node)) as this;
}

/** Upgrade the current View/ViewDU to a root node of new type */
upgradeToNewType(newType: T): Node {
throw Error(`Not implemented for type ${this.type.typeName}, new type ${newType.typeName}`);
}
}
41 changes: 40 additions & 1 deletion packages/ssz/src/view/container.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import {getNodeAtDepth, Gindex, LeafNode, Node, toGindexBitstring, Tree} from "@chainsafe/persistent-merkle-tree";
import {
getNodeAtDepth,
Gindex,
LeafNode,
Node,
toGindexBitstring,
Tree,
BranchNode,
zeroNode,
} from "@chainsafe/persistent-merkle-tree";
import {Type, ValueOf} from "../type/abstract";
import {isBasicType, BasicType} from "../type/basic";
import {isCompositeType, CompositeType} from "../type/composite";
Expand Down Expand Up @@ -63,6 +72,10 @@ class ContainerTreeView<Fields extends Record<string, Type<unknown>>> extends Tr
get node(): Node {
return this.tree.rootNode;
}

upgradeToNewType(newType: ContainerTypeGeneric<Fields>): Node {
return upgradeToNewType(this.node, this.type, newType);
}
}

export function getContainerTreeViewClass<Fields extends Record<string, Type<unknown>>>(
Expand Down Expand Up @@ -131,3 +144,29 @@ export function getContainerTreeViewClass<Fields extends Record<string, Type<unk

return CustomContainerTreeView as unknown as ContainerTreeViewTypeConstructor<Fields>;
}

/** Upgrade the current View/ViewDU to a root node of new type */
export function upgradeToNewType<Fields extends Record<string, Type<unknown>>>(
rootNode: Node,
oldType: ContainerTypeGeneric<Fields>,
newType: ContainerTypeGeneric<Fields>
): Node {
const newFieldsCount = newType.fieldsEntries.length;
const currentFieldsCount = oldType.fieldsEntries.length;
if (newFieldsCount < currentFieldsCount) {
throw Error(`Cannot convert to a type with fewer fields: ${newFieldsCount} < ${currentFieldsCount}`);
}

if (newType.depth === oldType.depth) {
// no need to grow the tree
return rootNode;
}

// grow the tree
let node = rootNode;
for (let depth = oldType.depth; depth < newType.depth; depth++) {
node = new BranchNode(node, zeroNode(depth));
}

return node;
}
8 changes: 7 additions & 1 deletion packages/ssz/src/viewDU/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import {ByteViews, Type} from "../type/abstract";
import {BasicType, isBasicType} from "../type/basic";
import {CompositeType, isCompositeType, CompositeTypeAny} from "../type/composite";
import {ContainerTypeGeneric} from "../view/container";
import {ContainerTypeGeneric, upgradeToNewType} from "../view/container";
import {TreeViewDU} from "./abstract";

/* eslint-disable @typescript-eslint/member-ordering */
Expand Down Expand Up @@ -185,6 +185,12 @@ class ContainerTreeViewDU<Fields extends Record<string, Type<unknown>>> extends

return variableIndex;
}

upgradeToNewType(newType: ContainerTypeGeneric<Fields>): Node {
this.commit();

return upgradeToNewType(this.node, this.type, newType);
}
}

export function getContainerTreeViewDUClass<Fields extends Record<string, Type<unknown>>>(
Expand Down
50 changes: 50 additions & 0 deletions packages/ssz/test/unit/byType/container/tree.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {expect} from "chai";
import {Tree} from "@chainsafe/persistent-merkle-tree";
import {
BitArray,
BitListType,
Expand All @@ -12,6 +13,7 @@ import {
ListCompositeType,
NoneType,
toHexString,
Type,
UintNumberType,
UnionType,
ValueOf,
Expand Down Expand Up @@ -635,3 +637,51 @@ describe("ContainerNodeStruct batchHashTreeRoot", function () {
expect(viewDU.batchHashTreeRoot()).to.be.deep.equal(expectedRoot);
});
});

describe("ContainerType.upgradeToNewType", () => {
const numFields = [2, 7, 15, 17, 31, 33, 63, 65, 127, 129];
for (const [i, numField] of numFields.entries()) {
it(`upgradeToNewType with ${numField} fields`, () => {
const fields: Record<string, Type<unknown>> = {};
for (let j = 0; j < numField; j++) {
fields[`f${j}`] = uint64NumInfType;
}
const oldType = new ContainerType(fields);
const view = oldType.defaultView();
const viewDU = oldType.defaultViewDU();
for (let j = 0; j < numField; j++) {
(view as Record<string, number>)[`f${j}`] = j;
(viewDU as Record<string, number>)[`f${j}`] = j;
}

for (let j = i + 1; j < numFields.length; j++) {
const newFields: Record<string, Type<unknown>> = {};
for (let k = 0; k < numFields[j]; k++) {
(newFields as Record<string, Type<unknown>>)[`f${k}`] = uint64NumInfType;
}

const newType = new ContainerType(newFields);
const newView = newType.getView(new Tree(view.upgradeToNewType(newType)));
const newViewDU = newType.getViewDU(viewDU.upgradeToNewType(newType));
for (let k = i + 1; k < numFields[j]; k++) {
(newView as Record<string, number>)[`f${k}`] = k;
(newViewDU as Record<string, number>)[`f${k}`] = k;
}
newViewDU.commit();

const expectedValue = newType.defaultValue();
for (let k = 0; k < numFields[j]; k++) {
(expectedValue as Record<string, number>)[`f${k}`] = k;
}
const expectedViewDU = newType.toViewDU(expectedValue);

expect(newView.toValue()).to.be.deep.equal(expectedValue);
expect(newView.hashTreeRoot()).to.be.deep.equal(expectedViewDU.hashTreeRoot());
expect(newView.serialize()).to.be.deep.equal(expectedViewDU.serialize());
expect(newViewDU.toValue()).to.be.deep.equal(expectedValue);
expect(newViewDU.hashTreeRoot()).to.be.deep.equal(expectedViewDU.hashTreeRoot());
expect(newViewDU.serialize()).to.be.deep.equal(expectedViewDU.serialize());
}
});
}
});

0 comments on commit 0fb8bbe

Please sign in to comment.