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

imprv: packages #399

Merged
merged 13 commits into from
Feb 5, 2025
6 changes: 3 additions & 3 deletions packages/node/src/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ async function updateHandler(node: DRPNode, sender: string, data: Uint8Array) {
if ((object.acl as ACL).permissionless) {
verifiedVertices = updateMessage.vertices;
} else {
verifiedVertices = await verifyIncomingVertices(object, updateMessage.vertices);
verifiedVertices = await verifyACLIncomingVertices(object, updateMessage.vertices);
}

const [merged, _] = object.merge(verifiedVertices);
Expand Down Expand Up @@ -251,7 +251,7 @@ async function syncAcceptHandler(node: DRPNode, sender: string, data: Uint8Array
if ((object.acl as ACL).permissionless) {
verifiedVertices = syncAcceptMessage.requested;
} else {
verifiedVertices = await verifyIncomingVertices(object, syncAcceptMessage.requested);
verifiedVertices = await verifyACLIncomingVertices(object, syncAcceptMessage.requested);
}

if (verifiedVertices.length !== 0) {
Expand Down Expand Up @@ -393,7 +393,7 @@ function getAttestations(object: DRPObject, vertices: Vertex[]): ObjectPb.Aggreg
.filter((a) => a !== undefined);
}

export async function verifyIncomingVertices(
export async function verifyACLIncomingVertices(
object: DRPObject,
incomingVertices: ObjectPb.Vertex[]
): Promise<Vertex[]> {
Expand Down
4 changes: 3 additions & 1 deletion packages/node/src/rpc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export function init(node: DRPNode) {
) {
let returnCode = 0;
try {
await node.subscribeObject(call.request.drpId);
await node.connectObject({
id: call.request.drpId,
});
} catch (e) {
log.error("::rpc::subscribeDRP: Error", e);
returnCode = 1;
Expand Down
6 changes: 3 additions & 3 deletions packages/node/tests/node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { beforeAll, beforeEach, describe, expect, test } from "vitest";
import {
signFinalityVertices,
signGeneratedVertices,
verifyIncomingVertices,
verifyACLIncomingVertices,
} from "../src/handlers.js";
import { DRPNode } from "../src/index.js";

Expand Down Expand Up @@ -84,7 +84,7 @@ describe("DPRNode with verify and sign signature", () => {
},
];
await signGeneratedVertices(drpNode, vertices);
const verifiedVertices = await verifyIncomingVertices(drpObject, vertices);
const verifiedVertices = await verifyACLIncomingVertices(drpObject, vertices);
expect(verifiedVertices.length).toBe(1);
});

Expand All @@ -103,7 +103,7 @@ describe("DPRNode with verify and sign signature", () => {
signature: new Uint8Array(),
},
];
const verifiedVertices = await verifyIncomingVertices(drpObject, vertices);
const verifiedVertices = await verifyACLIncomingVertices(drpObject, vertices);
expect(verifiedVertices.length).toBe(0);
});
});
Expand Down
3 changes: 3 additions & 0 deletions packages/object/src/hashgraph/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ export class HashGraph {
return; // Vertex already exists
}

if (vertex.dependencies.length === 0) {
throw new Error("Vertex dependencies are empty.");
}
for (const dep of vertex.dependencies) {
const depVertex = this.vertices.get(dep);
if (depVertex === undefined) {
Expand Down
38 changes: 38 additions & 0 deletions packages/object/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,15 @@ export class DRPObject implements ObjectPb.DRPObjectBase {
if (!this.hashGraph) {
throw new Error("Hashgraph is undefined");
}
if (!this.drp) {
return this._mergeWithoutDrp(vertices);
}
return this._mergeWithDrp(vertices);
}

/* Merges the vertices into the hashgraph using DRP
*/
private _mergeWithDrp(vertices: Vertex[]): [merged: boolean, missing: string[]] {
const missing = [];
for (const vertex of vertices) {
// Check to avoid manually crafted `undefined` operations
Expand Down Expand Up @@ -279,6 +288,35 @@ export class DRPObject implements ObjectPb.DRPObjectBase {
return [missing.length === 0, missing];
}

/* Merges the vertices into the hashgraph without using DRP
*/
private _mergeWithoutDrp(vertices: Vertex[]): [merged: boolean, missing: string[]] {
const missing = [];
for (const vertex of vertices) {
if (!vertex.operation || this.hashGraph.vertices.has(vertex.hash)) {
continue;
}

try {
if (!this._checkWriterPermission(vertex.peerId)) {
return [false, [vertex.hash]];
}
this.hashGraph.addVertex({
hash: vertex.hash,
operation: vertex.operation,
dependencies: vertex.dependencies,
peerId: vertex.peerId,
timestamp: vertex.timestamp,
signature: vertex.signature,
});
} catch (_) {
missing.push(vertex.hash);
}
}

return [missing.length === 0, missing];
}

subscribe(callback: DRPObjectCallback) {
this.subscriptions.push(callback);
}
Expand Down
113 changes: 87 additions & 26 deletions packages/object/tests/hashgraph.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MapConflictResolution, MapDRP } from "@ts-drp/blueprints/src/Map/index.js";
import { SetDRP } from "@ts-drp/blueprints/src/Set/index.js";
import { beforeEach, describe, expect, test } from "vitest";
import { beforeAll, beforeEach, describe, expect, test } from "vitest";

import { ObjectACL } from "../src/acl/index.js";
import { ACLGroup, DRPObject, DrpType, Hash, HashGraph, type Operation } from "../src/index.js";
Expand Down Expand Up @@ -105,31 +105,35 @@ describe("HashGraph construction tests", () => {
const drp1 = obj1.drp as SetDRP<number>;
drp1.add(1);
// add fake root
obj1.hashGraph.addVertex({
hash: "hash1",
peerId: "peer1",
operation: {
opType: "root",
value: null,
drpType: DrpType.DRP,
},
dependencies: [],
timestamp: Date.now(),
signature: new Uint8Array(),
});
obj1.hashGraph.addVertex({
hash: "hash2",
peerId: "peer1",
operation: {
opType: "add",
value: [1],
drpType: DrpType.DRP,
},
dependencies: ["hash1"],
timestamp: Date.now(),
signature: new Uint8Array(),
});
expect(selfCheckConstraints(obj1.hashGraph)).toBe(false);
expect(() => {
obj1.hashGraph.addVertex({
hash: "hash1",
peerId: "peer1",
operation: {
opType: "root",
value: null,
drpType: DrpType.DRP,
},
dependencies: [],
timestamp: Date.now(),
signature: new Uint8Array(),
});
}).toThrowError("Vertex dependencies are empty.");
expect(() => {
obj1.hashGraph.addVertex({
hash: "hash2",
peerId: "peer1",
operation: {
opType: "add",
value: [1],
drpType: DrpType.DRP,
},
dependencies: ["hash1"],
timestamp: Date.now(),
signature: new Uint8Array(),
});
}).toThrowError("Invalid dependency detected.");
expect(selfCheckConstraints(obj1.hashGraph)).toBe(true);

const linearOps = obj1.hashGraph.linearizeOperations();
const expectedOps: Operation[] = [{ opType: "add", value: [1], drpType: DrpType.DRP }];
Expand Down Expand Up @@ -362,6 +366,63 @@ describe("HashGraph for undefined operations tests", () => {
});
});

describe("Hashgraph and DRPObject merge without DRP tests", () => {
let obj1: DRPObject;
let obj2: DRPObject;
let obj3: DRPObject;
const acl = new ObjectACL({
admins: new Map([
["peer1", { ed25519PublicKey: "pubKey1", blsPublicKey: "pubKey1" }],
["peer2", { ed25519PublicKey: "pubKey2", blsPublicKey: "pubKey2" }],
]),
});

beforeAll(async () => {
obj1 = new DRPObject({ peerId: "peer1", acl, drp: new SetDRP<number>() });
obj2 = new DRPObject({ peerId: "peer2", acl, drp: new SetDRP<number>() });
obj3 = new DRPObject({ peerId: "peer3", acl });
});

test("Test object3 merge", () => {
// reproduce Test: Joao's latest brain teaser
/*
__ V2:ADD(2) -------------\
ROOT -- V1:ADD(1) / \ V5:RM(2)
\__ V3:RM(2) -- V4:RM(2) --/
*/

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

drp1.add(1);
obj2.merge(obj1.hashGraph.getAllVertices());

drp1.add(2);
drp2.delete(2);
drp2.delete(2);
obj1.merge(obj2.hashGraph.getAllVertices());
obj2.merge(obj1.hashGraph.getAllVertices());

drp1.delete(2);
obj2.merge(obj1.hashGraph.getAllVertices());

expect(drp1.query_has(1)).toBe(true);
expect(drp1.query_has(2)).toBe(false);
expect(obj1.hashGraph.vertices).toEqual(obj2.hashGraph.vertices);

const linearOps = obj1.hashGraph.linearizeOperations();
const expectedOps: Operation[] = [
{ opType: "add", value: [1], drpType: DrpType.DRP },
{ opType: "add", value: [2], drpType: DrpType.DRP },
{ opType: "delete", value: [2], drpType: DrpType.DRP },
];
expect(linearOps).toEqual(expectedOps);

obj3.merge(obj1.hashGraph.getAllVertices());
expect(obj3.hashGraph.vertices).toEqual(obj1.hashGraph.vertices);
});
});

describe("Vertex state tests", () => {
let obj1: DRPObject;
let obj2: DRPObject;
Expand Down
Loading