From 552b561c980812374d44ce4da7a64148714247a4 Mon Sep 17 00:00:00 2001 From: Chen Wen Kang <23054115+cwkang1998@users.noreply.github.com> Date: Tue, 24 Sep 2024 01:18:29 +0800 Subject: [PATCH] refactor: reuse protobuf Vertex definition (#164) Co-authored-by: Oak --- packages/blueprints/src/AddWinsSet/index.ts | 7 ++-- packages/object/src/hashgraph/index.ts | 20 ++++------- packages/object/src/index.ts | 23 ++++-------- packages/object/tests/hashgraph.test.ts | 40 ++++++++++++++++++++- pnpm-lock.yaml | 30 ++++++++-------- 5 files changed, 73 insertions(+), 47 deletions(-) diff --git a/packages/blueprints/src/AddWinsSet/index.ts b/packages/blueprints/src/AddWinsSet/index.ts index d0de413b..10dcc93a 100644 --- a/packages/blueprints/src/AddWinsSet/index.ts +++ b/packages/blueprints/src/AddWinsSet/index.ts @@ -44,9 +44,12 @@ export class AddWinsSet implements CRO { // in this case is an array of length 2 and there are only two possible operations resolveConflicts(vertices: Vertex[]): ResolveConflictsType { + // Both must have operations, if not return no-op if ( - vertices[0].operation.type !== vertices[1].operation.type && - vertices[0].operation.value === vertices[1].operation.value + vertices[0].operation && + vertices[1].operation && + vertices[0].operation?.type !== vertices[1].operation?.type && + vertices[0].operation?.value === vertices[1].operation?.value ) { return vertices[0].operation.type === "add" ? { action: ActionType.DropRight } diff --git a/packages/object/src/hashgraph/index.ts b/packages/object/src/hashgraph/index.ts index bf3674f6..95e1a412 100644 --- a/packages/object/src/hashgraph/index.ts +++ b/packages/object/src/hashgraph/index.ts @@ -1,13 +1,15 @@ import * as crypto from "node:crypto"; import { linearizeMultiple } from "../linearize/multipleSemantics.js"; import { linearizePair } from "../linearize/pairSemantics.js"; +import { Vertex_Operation as Operation, Vertex } from "../proto/object_pb.js"; import { BitSet } from "./bitset.js"; +// Reexporting the Vertex and Operation types from the protobuf file +export { Vertex, Operation }; + export type Hash = string; -// biome-ignore lint: value can't be unknown because of protobuf -export type Operation = { type: string; value: any | null }; -enum OperationType { +export enum OperationType { NOP = "-1", } @@ -30,15 +32,6 @@ export type ResolveConflictsType = { vertices?: Hash[]; }; -export interface Vertex { - hash: Hash; - nodeId: string; - // internal Operation type enum converted to number - // -1 for NOP - operation: Operation; - dependencies: Hash[]; -} - export class HashGraph { nodeId: string; resolveConflicts: (vertices: Vertex[]) => ResolveConflictsType; @@ -89,10 +82,11 @@ export class HashGraph { addToFrontier(operation: Operation): Vertex { const deps = this.getFrontier(); const hash = computeHash(this.nodeId, operation, deps); + const vertex: Vertex = { hash, nodeId: this.nodeId, - operation, + operation: operation ?? { type: OperationType.NOP }, dependencies: deps, }; diff --git a/packages/object/src/index.ts b/packages/object/src/index.ts index 22cb511b..91de19fd 100644 --- a/packages/object/src/index.ts +++ b/packages/object/src/index.ts @@ -1,6 +1,5 @@ import * as crypto from "node:crypto"; import { - type ActionType, HashGraph, type Operation, type ResolveConflictsType, @@ -91,10 +90,7 @@ export class TopologyObject implements ITopologyObject { const serializedVertex = ObjectPb.Vertex.create({ hash: vertex.hash, nodeId: vertex.nodeId, - operation: { - type: vertex.operation.type, - value: vertex.operation.value, - }, + operation: vertex.operation, dependencies: vertex.dependencies, }); this.vertices.push(serializedVertex); @@ -103,6 +99,11 @@ export class TopologyObject implements ITopologyObject { merge(vertices: Vertex[]) { for (const vertex of vertices) { + // Check to avoid manually crafted `undefined` operations + if (!vertex.operation) { + continue; + } + this.hashGraph.addVertex( vertex.operation, vertex.dependencies, @@ -111,17 +112,7 @@ export class TopologyObject implements ITopologyObject { } const operations = this.hashGraph.linearizeOperations(); - this.vertices = this.hashGraph.getAllVertices().map((vertex) => { - return { - hash: vertex.hash, - nodeId: vertex.nodeId, - operation: { - type: vertex.operation.type, - value: vertex.operation.value, - }, - dependencies: vertex.dependencies, - }; - }); + this.vertices = this.hashGraph.getAllVertices(); (this.cro as CRO).mergeCallback(operations); this._notify("merge", this.vertices); diff --git a/packages/object/tests/hashgraph.test.ts b/packages/object/tests/hashgraph.test.ts index 416497bf..b1fde2f6 100644 --- a/packages/object/tests/hashgraph.test.ts +++ b/packages/object/tests/hashgraph.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, test } from "vitest"; import { AddWinsSet } from "../../blueprints/src/AddWinsSet/index.js"; import { PseudoRandomWinsSet } from "../../blueprints/src/PseudoRandomWinsSet/index.js"; -import { TopologyObject } from "../src/index.js"; +import { type Operation, OperationType, TopologyObject } from "../src/index.js"; describe("HashGraph for AddWinSet tests", () => { let obj1: TopologyObject; @@ -349,3 +349,41 @@ describe("HashGraph for PseudoRandomWinsSet tests", () => { expect(linearOps).toEqual([{ type: "add", value: 3 }]); }); }); + +describe("HashGraph for undefined operations tests", () => { + let obj1: TopologyObject; + let obj2: TopologyObject; + + beforeEach(async () => { + obj1 = new TopologyObject("peer1", new AddWinsSet()); + obj2 = new TopologyObject("peer2", new AddWinsSet()); + }); + + test("Test: merge should skip undefined operations", () => { + const cro1 = obj1.cro as AddWinsSet; + const cro2 = obj2.cro as AddWinsSet; + + cro1.add(1); + cro2.add(2); + + // Set one of the vertice from cro1 to have undefined operation + obj1.hashGraph.getAllVertices()[1].operation = undefined; + + obj2.merge(obj1.hashGraph.getAllVertices()); + + const linearOps = obj2.hashGraph.linearizeOperations(); + // Should only have one, since we skipped the undefined operations + expect(linearOps).toEqual([{ type: "add", value: 2 }]); + }); + + test("Test: addToFrontier with undefined operation return Vertex with NoOp operation", () => { + // Forcefully pass an undefined value + const createdVertex = obj1.hashGraph.addToFrontier( + undefined as unknown as Operation, + ); + + expect(createdVertex.operation).toEqual({ + type: OperationType.NOP, + } as Operation); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d224d9a7..7e8ca6c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,16 +45,16 @@ importers: examples/canvas: dependencies: '@topology-foundation/blueprints': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../../packages/blueprints '@topology-foundation/network': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../../packages/network '@topology-foundation/node': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../../packages/node '@topology-foundation/object': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../../packages/object crypto-browserify: specifier: ^3.12.0 @@ -91,16 +91,16 @@ importers: examples/chat: dependencies: '@topology-foundation/blueprints': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../../packages/blueprints '@topology-foundation/network': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../../packages/network '@topology-foundation/node': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../../packages/node '@topology-foundation/object': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../../packages/object assemblyscript: specifier: ^0.27.29 @@ -146,13 +146,13 @@ importers: examples/grid: dependencies: '@topology-foundation/network': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../../packages/network '@topology-foundation/node': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../../packages/node '@topology-foundation/object': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../../packages/object assemblyscript: specifier: ^0.27.29 @@ -205,7 +205,7 @@ importers: version: 4.0.3 devDependencies: '@topology-foundation/object': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../object assemblyscript: specifier: ^0.27.29 @@ -305,13 +305,13 @@ importers: specifier: ^1.7.0 version: 1.7.0 '@topology-foundation/blueprints': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../blueprints '@topology-foundation/network': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../network '@topology-foundation/object': - specifier: 0.1.1 + specifier: 0.2.0 version: link:../object commander: specifier: ^12.1.0