diff --git a/packages/ssz/src/viewDU/arrayComposite.ts b/packages/ssz/src/viewDU/arrayComposite.ts index 1a25647d..aa7e2be4 100644 --- a/packages/ssz/src/viewDU/arrayComposite.ts +++ b/packages/ssz/src/viewDU/arrayComposite.ts @@ -145,14 +145,16 @@ export class ArrayCompositeTreeViewDU< } /** - * WARNING: Returns all commited changes, if there are any pending changes commit them beforehand + * Returns all elements at every index, if an index is modified it will return the modified view. + * No need to commit() before calling this function. */ getAllReadonly(): CompositeViewDU[] { - this.populateAllNodes(); + this.populateAllOldNodes(); const views = new Array>(this._length); for (let i = 0; i < this._length; i++) { - views[i] = this.type.elementType.getViewDU(this.nodes[i], this.caches[i]); + // this will get pending change first, if not it will get from the `this.nodes` array + views[i] = this.getReadonly(i); } return views; } @@ -256,4 +258,16 @@ export class ArrayCompositeTreeViewDU< this.nodesPopulated = true; } } + + /** + * Similar to `populateAllNodes` but this does not require a commit() before reading all nodes. + * If there are pendingChanges, they will NOT be included in the `nodes` array. + */ + protected populateAllOldNodes(): void { + if (!this.nodesPopulated) { + const originalLength = this.dirtyLength ? this.type.tree_getLength(this._rootNode) : this._length; + this.nodes = getNodesAtDepth(this._rootNode, this.type.depth, 0, originalLength); + this.nodesPopulated = true; + } + } } diff --git a/packages/ssz/test/unit/byType/listComposite/tree.test.ts b/packages/ssz/test/unit/byType/listComposite/tree.test.ts index 2a15c2a8..404d5b01 100644 --- a/packages/ssz/test/unit/byType/listComposite/tree.test.ts +++ b/packages/ssz/test/unit/byType/listComposite/tree.test.ts @@ -120,7 +120,8 @@ describe("ListCompositeType tree reads", () => { // Only for viewDU if (view instanceof ArrayCompositeTreeViewDU) { - expect(() => view.getAllReadonly()).toThrow("Must commit changes before reading all nodes"); + expect(() => view.getAllReadonly()).not.toThrow(); + expect(() => view.getAllReadonlyValues()).toThrow("Must commit changes before reading all nodes"); view.commit(); } @@ -337,3 +338,24 @@ describe("ListCompositeType batchHashTreeRoot", () => { }); } }); + +describe("ListCompositeType getAllReadOnly - no commit", () => { + it("getAllReadOnly() without commit", () => { + const listType = new ListCompositeType(ssz.Root, 1024); + const listLength = 2; + const list = Array.from({length: listLength}, (_, i) => Buffer.alloc(32, i)); + const listView = listType.toViewDU(list); + expect(listView.getAllReadonly()).to.deep.equal(list); + + // modify + listView.set(0, Buffer.alloc(32, 1)); + // push + listView.push(Buffer.alloc(32, 1)); + + // getAllReadOnly() without commit, now all items should be the same + expect(listView.getAllReadonly()).to.deep.equal(Array.from({length: 3}, () => Buffer.alloc(32, 1))); + + // getAllReadOnlyValues() will throw + expect(() => listView.getAllReadonlyValues()).toThrow("Must commit changes before reading all nodes"); + }); +});