From 62ae879364c3915c2d46e047caec793af90a38b3 Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Tue, 31 May 2022 12:37:52 +0700 Subject: [PATCH] Fix ListCompositeType.sliceTo(-1) (#268) --- packages/persistent-merkle-tree/src/tree.ts | 6 ++++++ packages/ssz/src/viewDU/listComposite.ts | 2 ++ .../unit/byType/listComposite/tree.test.ts | 18 +++++++++++++----- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/packages/persistent-merkle-tree/src/tree.ts b/packages/persistent-merkle-tree/src/tree.ts index 456ed085..e2687310 100644 --- a/packages/persistent-merkle-tree/src/tree.ts +++ b/packages/persistent-merkle-tree/src/tree.ts @@ -643,6 +643,12 @@ export function treeZeroAfterIndex(rootNode: Node, nodesDepth: number, index: nu // goRight = (N & mask) == mask // ``` + // Degenerate case where tree is zero after a negative index (-1). + // All positive indexes are zero, so the entire tree is zero. Return cached zero node as root. + if (index < 0) { + return zeroNode(nodesDepth); + } + /** * Contiguous filled stack of parent nodes. It get filled in the first descent * Indexed by depthi diff --git a/packages/ssz/src/viewDU/listComposite.ts b/packages/ssz/src/viewDU/listComposite.ts index b4dfbbaf..b9f87caf 100644 --- a/packages/ssz/src/viewDU/listComposite.ts +++ b/packages/ssz/src/viewDU/listComposite.ts @@ -39,6 +39,8 @@ export class ListCompositeTreeViewDU< * ``` * * To achieve it, rebinds the underlying tree zero-ing all nodes right of `index`. + * + * Note: Using index = -1, returns an empty list of length 0. */ sliceTo(index: number): this { // Commit before getting rootNode to ensure all pending data is in the rootNode diff --git a/packages/ssz/test/unit/byType/listComposite/tree.test.ts b/packages/ssz/test/unit/byType/listComposite/tree.test.ts index a60b325e..573db18e 100644 --- a/packages/ssz/test/unit/byType/listComposite/tree.test.ts +++ b/packages/ssz/test/unit/byType/listComposite/tree.test.ts @@ -173,16 +173,24 @@ describe("ListCompositeType.sliceTo", () => { const listType = new ListCompositeType(ssz.Root, 1024); const listView = listType.defaultViewDU(); const listRoots: string[] = []; + const listSerialized: string[] = []; - for (let i = 0; i < 16; i++) { - listView.push(Buffer.alloc(32, i + 1)); - listRoots.push(toHexString(listView.serialize())); + for (let i = -1; i < 16; i++) { + // Skip first loop to persist empty case + if (i >= 0) { + listView.push(Buffer.alloc(32, 0xf + i)); // Avoid 0 case + } + // Javascript arrays can handle negative indexes (ok for tests) + listSerialized[i] = toHexString(listView.serialize()); + listRoots[i] = toHexString(listView.hashTreeRoot()); } - for (let i = 0; i < 16; i++) { + // Start at -1 to test the empty case. + for (let i = -1; i < 16; i++) { const listSlice = listView.sliceTo(i); expect(listSlice.length).to.equal(i + 1, `Wrong length at .sliceTo(${i})`); - expect(toHexString(listSlice.serialize())).equals(listRoots[i], `Wrong bytes at .sliceTo(${i})`); + expect(toHexString(listSlice.serialize())).equals(listSerialized[i], `Wrong serialize at .sliceTo(${i})`); + expect(toHexString(listSlice.hashTreeRoot())).equals(listRoots[i], `Wrong root at .sliceTo(${i})`); } }); });