Skip to content

Commit

Permalink
Fix ContainerLeafNodeStructTreeValue (#179)
Browse files Browse the repository at this point in the history
* Fix ContainerLeafNodeStructTreeValue

* Add .valueOf() test
  • Loading branch information
dapplion authored Aug 30, 2021
1 parent 057e9a6 commit ef247f3
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 73 deletions.
76 changes: 75 additions & 1 deletion src/backings/tree/treeValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
isVectorType,
isUnionType,
UnionType,
isContainerLeafNodeStructType,
} from "../../types";
import {byteArrayEquals} from "../../util/byteArray";
import {isTree} from "../../util/tree";
Expand Down Expand Up @@ -55,7 +56,11 @@ export function getTreeValueClass<T extends CompositeValue>(type: CompositeType<
return (CompositeArrayTreeValue as unknown) as TreeValueConstructor<T>;
}
} else if (isContainerType(type)) {
return (ContainerTreeValue as unknown) as TreeValueConstructor<T>;
if (isContainerLeafNodeStructType(type)) {
return (ContainerLeafNodeStructTreeValue as unknown) as TreeValueConstructor<T>;
} else {
return (ContainerTreeValue as unknown) as TreeValueConstructor<T>;
}
} else if (isUnionType(type)) {
return (UnionTreeValue as unknown) as TreeValueConstructor<T>;
}
Expand Down Expand Up @@ -557,6 +562,75 @@ export class ContainerTreeValue<T extends CompositeValue> extends TreeValue<T> {
}
}

/**
* Custom TreeValue to be used in `ContainerLeafNodeStructType`.
*
* It skips extra work done in `ContainerTreeValue` since all data is represented as struct and should be returned
* as struct, not as TreeBacked.
*/
export class ContainerLeafNodeStructTreeValue<T extends CompositeValue> extends TreeValue<T> {
type: ContainerType<T>;

constructor(type: ContainerType<T>, tree: Tree) {
super(type, tree);
this.type = type;
}

getProperty<P extends keyof T>(property: P): ValueOf<T, P> {
return this.type.tree_getProperty(this.tree, property) as ValueOf<T, P>;
}

setProperty<P extends keyof T>(property: P, value: ValueOf<T, P>): boolean {
return this.type.tree_setProperty(this.tree, property, value);
}

*keys(): IterableIterator<string> {
yield* this.getPropertyNames() as string[];
}

*values(): IterableIterator<ValueOf<T>> {
for (const [_key, value] of this.entries()) {
yield value;
}
}

*entries(): IterableIterator<[string, ValueOf<T>]> {
const keys = this.getPropertyNames();
let i = 0;
for (const value of this.type.tree_iterateValues(this.tree)) {
const propName = keys[i] as keyof T;
yield [propName as string, value as ValueOf<T>];
i++;
}
}

*readonlyValues(): IterableIterator<ValueOf<T>> {
return yield* this.values();
}

*readonlyEntries(): IterableIterator<[string, ValueOf<T>]> {
return yield* this.entries();
}

keysArray(): string[] {
return this.getPropertyNames() as string[];
}
valuesArray(): ValueOf<T>[] {
return this.type.tree_getValues(this.tree) as ValueOf<T>[];
}
entriesArray(): [string, ValueOf<T>][] {
const keys = this.getPropertyNames();
const values = this.type.tree_getValues(this.tree);
return keys.map((key, i) => [key as string, values[i] as ValueOf<T>]);
}
readonlyValuesArray(): ValueOf<T>[] {
return this.valuesArray();
}
readonlyEntriesArray(): [string, ValueOf<T>][] {
return this.entriesArray();
}
}

export class UnionTreeValue<T extends Union<unknown>> extends TreeValue<T> {
type: UnionType<T>;

Expand Down
93 changes: 21 additions & 72 deletions src/types/composite/containerLeafNodeStruct.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import {CompositeValue, List, ObjectLike} from "../../interface";
import {Node, BranchNode, Tree, hashObjectToUint8Array} from "@chainsafe/persistent-merkle-tree";
import {CompositeArrayTreeValue, TreeProxyHandler, TreeValue} from "../../backings/tree/treeValue";
import {TreeBacked, ValueOf} from "../../backings";
import {
CompositeArrayTreeValue,
ContainerLeafNodeStructTreeValue,
TreeProxyHandler,
} from "../../backings/tree/treeValue";
import {TreeBacked} from "../../backings";
import {HashObject} from "@chainsafe/as-sha256";
import {ContainerType} from "./container";
import {ContainerType, IContainerOptions} from "./container";
import {isTypeOf, Type} from "../type";

export const CONTAINER_LEAF_NODE_STRUCT_TYPE = Symbol.for("ssz/ContainerLeafNodeStructType");

export function isContainerLeafNodeStructType<T extends ObjectLike = ObjectLike>(
type: Type<unknown>
): type is ContainerLeafNodeStructType<T> {
return isTypeOf(type, CONTAINER_LEAF_NODE_STRUCT_TYPE);
}

/**
* Container that when represented as a Tree its children's data is represented as a struct, not a tree.
Expand All @@ -13,6 +26,11 @@ import {ContainerType} from "./container";
* expensive because the tree has to be recreated every time.
*/
export class ContainerLeafNodeStructType<T extends ObjectLike = ObjectLike> extends ContainerType<T> {
constructor(options: IContainerOptions) {
super(options);
this._typeSymbols.add(CONTAINER_LEAF_NODE_STRUCT_TYPE);
}

/** Method to allow the Node to merkelize the struct */
toFullTree(value: T): Tree {
return super.struct_convertToTree(value);
Expand Down Expand Up @@ -165,75 +183,6 @@ export class BranchNodeStruct<T> extends Node {
}
}

/**
* Custom TreeValue to be used in `ContainerLeafNodeStructType`.
*
* It skips extra work done in `ContainerTreeValue` since all data is represented as struct and should be returned
* as struct, not as TreeBacked.
*/
export class ContainerLeafNodeStructTreeValue<T extends CompositeValue> extends TreeValue<T> {
type: ContainerType<T>;

constructor(type: ContainerType<T>, tree: Tree) {
super(type, tree);
this.type = type;
}

getProperty<P extends keyof T>(property: P): ValueOf<T, P> {
return this.type.tree_getProperty(this.tree, property) as ValueOf<T, P>;
}

setProperty<P extends keyof T>(property: P, value: ValueOf<T, P>): boolean {
return this.type.tree_setProperty(this.tree, property, value);
}

*keys(): IterableIterator<string> {
yield* this.getPropertyNames() as string[];
}

*values(): IterableIterator<ValueOf<T>> {
for (const [_key, value] of this.entries()) {
yield value;
}
}

*entries(): IterableIterator<[string, ValueOf<T>]> {
const keys = this.getPropertyNames();
let i = 0;
for (const value of this.type.tree_iterateValues(this.tree)) {
const propName = keys[i] as keyof T;
yield [propName as string, value as ValueOf<T>];
i++;
}
}

*readonlyValues(): IterableIterator<ValueOf<T>> {
return yield* this.values();
}

*readonlyEntries(): IterableIterator<[string, ValueOf<T>]> {
return yield* this.entries();
}

keysArray(): string[] {
return this.getPropertyNames() as string[];
}
valuesArray(): ValueOf<T>[] {
return this.type.tree_getValues(this.tree) as ValueOf<T>[];
}
entriesArray(): [string, ValueOf<T>][] {
const keys = this.getPropertyNames();
const values = this.type.tree_getValues(this.tree);
return keys.map((key, i) => [key as string, values[i] as ValueOf<T>]);
}
readonlyValuesArray(): ValueOf<T>[] {
return this.valuesArray();
}
readonlyEntriesArray(): [string, ValueOf<T>][] {
return this.entriesArray();
}
}

/**
* Custom readonlyValues to return non-tree backed values, but the raw struct inside BranchNodeStruct nodes.
*
Expand Down
14 changes: 14 additions & 0 deletions test/unit/validators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,5 +120,19 @@ describe("Container with BranchNodeStruct", function () {

expect(readonlyValuesListOfLeafNodeStruct(validatorListTB)).to.deep.equal(validatorsFlat);
});

it("return each property as struct backed", () => {
const validatorListTB = ValidtorsListType.defaultTreeBacked();
validatorListTB.push(validator);
for (const key of Object.keys(validator) as (keyof typeof validator)[]) {
expect(validatorListTB[0][key].valueOf()).to.deep.equal(validator[key], `wrong ${key} value`);
}
});

it("validator.valueOf()", () => {
const validatorListTB = ValidtorsListType.defaultTreeBacked();
validatorListTB.push(validator);
expect(validatorListTB[0].valueOf()).to.deep.equal(validator);
});
});
});

0 comments on commit ef247f3

Please sign in to comment.