Skip to content

Commit

Permalink
fix: dfs, test for cycle
Browse files Browse the repository at this point in the history
  • Loading branch information
JanLewDev committed Feb 18, 2025
1 parent 0799ba3 commit 644cf5a
Show file tree
Hide file tree
Showing 4 changed files with 298 additions and 243 deletions.
2 changes: 1 addition & 1 deletion packages/object/src/hashgraph/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ export class HashGraph {
if (visited.has(node)) {
stack.pop();
result.push(node);
processing.delete(node);
continue;
}

Expand All @@ -190,7 +191,6 @@ export class HashGraph {
}
}
}
processing.delete(node);
}

return result.reverse();
Expand Down
55 changes: 54 additions & 1 deletion packages/object/tests/hashgraph.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { SetDRP } from "@ts-drp/blueprints/src/Set/index.js";
import { beforeAll, beforeEach, describe, expect, test, vi } from "vitest";

import { ObjectACL } from "../src/acl/index.js";
import { Vertex } from "../src/hashgraph/index.js";
import { ActionType, SemanticsType, Vertex } from "../src/hashgraph/index.js";
import {
ACLGroup,
DRP,
Expand Down Expand Up @@ -116,6 +116,59 @@ describe("HashGraph construction tests", () => {
] as Operation[]);
});

test("Test: Should detect cycle in topological sort", () => {
const hashgraph = new HashGraph(
"",
(_vertices: Vertex[]) => {
return {
action: ActionType.Nop,
};
},
(_vertices: Vertex[]) => {
return {
action: ActionType.Nop,
};
},
SemanticsType.pair
);
const frontier = hashgraph.getFrontier();
const v1 = newVertex(
"",
{
opType: "test",
value: [1],
drpType: DrpType.DRP,
},
frontier,
Date.now(),
new Uint8Array()
);
hashgraph.addVertex(v1);

const v2 = newVertex(
"",
{
opType: "test",
value: [2],
drpType: DrpType.DRP,
},
[v1.hash],
Date.now(),
new Uint8Array()
);
hashgraph.addVertex(v2);

// create a cycle
hashgraph.forwardEdges.set(v2.hash, [HashGraph.rootHash]);

expect(() => {
hashgraph.dfsTopologicalSortIterative(
HashGraph.rootHash,
new ObjectSet(hashgraph.vertices.keys())
);
}).toThrowError("Graph contains a cycle!");
});

test("Test: HashGraph with 2 root vertices", () => {
/*
ROOT -- V1:ADD(1)
Expand Down
244 changes: 243 additions & 1 deletion packages/object/tests/linearize.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, test, vi } from "vitest";
import { describe, expect, test, vi, beforeEach, afterEach } from "vitest";

import { ActionType } from "../dist/src/hashgraph/index.js";
import { SemanticsType } from "../dist/src/hashgraph/index.js";
Expand Down Expand Up @@ -134,3 +134,245 @@ describe("Linearize correctly", () => {
}
});
});

describe("linearizeMultipleSemantics", () => {
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date(Date.UTC(1998, 11, 19)));
});

afterEach(() => {
vi.clearAllMocks();
});

test("should linearize operations in a simple sequence", () => {
const hashGraph = new HashGraph(
"",
(_vertices: Vertex[]) => ({
action: ActionType.Nop,
}),
() => ({
action: ActionType.Nop,
}),
SemanticsType.multiple
);
const origin = HashGraph.rootHash;

// Add vertices to the graph
hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [1],
drpType: DrpType.DRP,
},
hashGraph.getFrontier(),
Date.now(),
new Uint8Array()
)
);

hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [2],
drpType: DrpType.DRP,
},
hashGraph.getFrontier(),
Date.now(),
new Uint8Array()
)
);

const subgraph = new ObjectSet<string>();
hashGraph.getAllVertices().forEach((vertex) => subgraph.add(vertex.hash));

const result = linearizeMultipleSemantics(hashGraph, origin, subgraph);
expect(result.map((op) => op.value)).toEqual([[1], [2]]);
});

test("should handle concurrent operations with conflict resolution", () => {
const hashGraph = new HashGraph(
"",
(_vertices: Vertex[]) => ({
action: ActionType.Drop,
vertices: _vertices.filter((_, index) => index !== 0).map((vertex) => vertex.hash),
}),
(_vertices: Vertex[]) => ({
action: ActionType.Drop,
vertices: _vertices.filter((_, index) => index !== 0).map((vertex) => vertex.hash),
}),
SemanticsType.multiple
);
const origin = HashGraph.rootHash;
let frontier = hashGraph.getFrontier();

// Add concurrent vertices
hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [1],
drpType: DrpType.DRP,
},
frontier,
Date.now(),
new Uint8Array()
)
);

hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [2],
drpType: DrpType.DRP,
},
frontier,
Date.now(),
new Uint8Array()
)
);

hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [3],
drpType: DrpType.DRP,
},
frontier,
Date.now(),
new Uint8Array()
)
);

// Get the frontier
frontier = hashGraph.getFrontier();

hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [4],
drpType: DrpType.DRP,
},
frontier,
Date.now(),
new Uint8Array()
)
);
console.log(`frontier: ${frontier}`);
hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [5],
drpType: DrpType.DRP,
},
frontier.filter((_, idx) => idx !== 0),
Date.now(),
new Uint8Array()
)
);

frontier = hashGraph.getFrontier();

hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [6],
drpType: DrpType.DRP,
},
frontier,
Date.now(),
new Uint8Array()
)
);

const subgraph = new ObjectSet<string>();
hashGraph.getAllVertices().forEach((vertex) => subgraph.add(vertex.hash));

const result = linearizeMultipleSemantics(hashGraph, origin, subgraph);
expect(result.map((op) => op.value)).toEqual([[3], [5], [6]]);
});

test("should handle operations with null values", () => {
const hashGraph = new HashGraph(
"",
() => ({
action: ActionType.Nop,
}),
() => ({
action: ActionType.Nop,
}),
SemanticsType.multiple
);
const origin = HashGraph.rootHash;

// Add vertices to the graph
hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: null,
drpType: DrpType.DRP,
},
hashGraph.getFrontier(),
Date.now(),
new Uint8Array()
)
);

hashGraph.addVertex(
newVertex(
"",
{
opType: "set",
value: [2],
drpType: DrpType.DRP,
},
hashGraph.getFrontier(),
Date.now(),
new Uint8Array()
)
);

const subgraph = new ObjectSet<string>();
hashGraph.getAllVertices().forEach((vertex) => subgraph.add(vertex.hash));

const result = linearizeMultipleSemantics(hashGraph, origin, subgraph);
expect(result.map((op) => op.value)).toEqual([[2]]);
});

test("should handle empty subgraph", () => {
const hashGraph = new HashGraph(
"",
() => ({
action: ActionType.Nop,
}),
() => ({
action: ActionType.Nop,
}),
SemanticsType.multiple
);
const origin = HashGraph.rootHash;

const subgraph = new ObjectSet<string>();
subgraph.add(origin);

const result = linearizeMultipleSemantics(hashGraph, origin, subgraph);
expect(result).toEqual([]);
});
});
Loading

0 comments on commit 644cf5a

Please sign in to comment.