Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add timestamp to vertex #288

Merged
merged 23 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion packages/network/src/proto/drp/object/v1/object_pb.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/node/src/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ async function updateHandler(node: DRPNode, data: Uint8Array, sender: string) {
value: v.operation?.value,
},
dependencies: v.dependencies,
timestamp: v.timestamp,
};
}),
);
Expand Down Expand Up @@ -147,6 +148,7 @@ function syncAcceptHandler(node: DRPNode, sender: string, data: Uint8Array) {
value: v.operation?.value,
},
dependencies: v.dependencies,
timestamp: v.timestamp,
};
});

Expand Down
29 changes: 24 additions & 5 deletions packages/object/src/hashgraph/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export class HashGraph {
value: null,
},
dependencies: [],
timestamp: 0,
};
this.vertices.set(HashGraph.rootHash, rootVertex);
this.frontier.push(HashGraph.rootHash);
Expand All @@ -107,6 +108,7 @@ export class HashGraph {
nodeId: this.nodeId,
operation: operation ?? { type: OperationType.NOP },
dependencies: deps,
timestamp: Date.now(),
};

this.vertices.set(hash, vertex);
Expand Down Expand Up @@ -145,23 +147,40 @@ export class HashGraph {
* If the vertex already exists, return the hash of the existing vertex.
* Throws an error if any of the dependencies are not present in the hashgraph.
*/
addVertex(operation: Operation, deps: Hash[], nodeId: string): Hash {
addVertex(
operation: Operation,
deps: Hash[],
nodeId: string,
timestamp: number,
): Hash {
const hash = computeHash(nodeId, operation, deps);
if (this.vertices.has(hash)) {
return hash; // Vertex already exists
}

if (
!deps.every((dep) => this.forwardEdges.has(dep) || this.vertices.has(dep))
) {
throw new Error("Invalid dependency detected.");
for (const dep of deps) {
const vertex = this.vertices.get(dep);
if (vertex === undefined) {
throw new Error("Invalid dependency detected.");
}
if (vertex.timestamp > timestamp) {
magnified103 marked this conversation as resolved.
Show resolved Hide resolved
d-roak marked this conversation as resolved.
Show resolved Hide resolved
// Vertex's timestamp must not be less than any of its dependencies' timestamps
throw new Error("Invalid timestamp detected.");
}
}

const currentTimestamp = Date.now();
if (timestamp > currentTimestamp) {
// Vertex created in the future is invalid
throw new Error("Invalid timestamp detected.");
}

const vertex: Vertex = {
hash,
nodeId,
operation,
dependencies: deps,
timestamp,
};
this.vertices.set(hash, vertex);
this.frontier.push(hash);
Expand Down
2 changes: 2 additions & 0 deletions packages/object/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export class DRPObject implements IDRPObject {
nodeId: vertex.nodeId,
operation: vertex.operation,
dependencies: vertex.dependencies,
timestamp: vertex.timestamp,
});
this.vertices.push(serializedVertex);
this._notify("callFn", [serializedVertex]);
Expand All @@ -148,6 +149,7 @@ export class DRPObject implements IDRPObject {
vertex.operation,
vertex.dependencies,
vertex.nodeId,
vertex.timestamp,
);

this._setState(vertex);
Expand Down
1 change: 1 addition & 0 deletions packages/object/src/proto/drp/object/v1/object.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ message Vertex {
string node_id = 2;
Operation operation = 3;
repeated string dependencies = 4;
int64 timestamp = 5;
}

message DRPObjectBase {
Expand Down
30 changes: 29 additions & 1 deletion packages/object/src/proto/drp/object/v1/object_pb.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions packages/object/tests/hashgraph.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ describe("HashGraph construction tests", () => {
},
[],
"",
Date.now(),
);
obj1.hashGraph.addVertex(
{
Expand All @@ -77,6 +78,7 @@ describe("HashGraph construction tests", () => {
},
[hash],
"",
Date.now(),
);
expect(obj1.hashGraph.selfCheckConstraints()).toBe(false);

Expand Down Expand Up @@ -617,3 +619,66 @@ describe("Vertex state tests", () => {
expect(drpStateV8?.state.get("state").get(3)).toBe(undefined);
});
});

describe("Vertex timestamp tests", () => {
let obj1: DRPObject;
let obj2: DRPObject;
let obj3: DRPObject;

beforeEach(async () => {
obj1 = new DRPObject("peer1", new AddWinsSet<number>());
obj2 = new DRPObject("peer1", new AddWinsSet<number>());
obj3 = new DRPObject("peer1", new AddWinsSet<number>());
});

test("Test: Vertex created in the future is invalid", () => {
const drp1 = obj1.drp as AddWinsSet<number>;

drp1.add(1);

expect(() =>
obj1.hashGraph.addVertex(
{
type: "add",
value: 1,
},
obj1.hashGraph.getFrontier(),
"",
Number.POSITIVE_INFINITY,
),
).toThrowError("Invalid timestamp detected.");
});

test("Test: Vertex's timestamp must not be less than any of its dependencies' timestamps", () => {
/*
__ V1:ADD(1) __
/ \
ROOT -- V2:ADD(2) ---- V1:ADD(4) (invalid)
magnified103 marked this conversation as resolved.
Show resolved Hide resolved
\ /
-- V3:ADD(3) --
*/

const drp1 = obj1.drp as AddWinsSet<number>;
const drp2 = obj2.drp as AddWinsSet<number>;
const drp3 = obj2.drp as AddWinsSet<number>;

drp1.add(1);
drp2.add(2);
drp3.add(3);

obj1.merge(obj2.hashGraph.getAllVertices());
obj1.merge(obj3.hashGraph.getAllVertices());

expect(() =>
obj1.hashGraph.addVertex(
{
type: "add",
value: 1,
},
obj1.hashGraph.getFrontier(),
"",
1,
),
).toThrowError("Invalid timestamp detected.");
});
});
Loading