Skip to content

Commit

Permalink
Merge branch 'main' into feat/check-writer-permisson
Browse files Browse the repository at this point in the history
  • Loading branch information
trungnotchung committed Jan 3, 2025
2 parents c03a472 + 79133d0 commit adfb540
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 17 deletions.
2 changes: 1 addition & 1 deletion packages/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"@ts-drp/network": "0.4.4",
"@ts-drp/object": "0.4.4",
"@ts-drp/logger": "0.4.4",
"commander": "^12.1.0",
"commander": "^13.0.0",
"uint8arrays": "^5.1.0"
}
}
56 changes: 43 additions & 13 deletions packages/object/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,35 @@ export class DRPObject implements IDRPObject {
}

// This function is black magic, it allows us to intercept calls to the DRP object
proxyDRPHandler(): ProxyHandler<object> {
proxyDRPHandler(parentProp?: string): ProxyHandler<object> {
const obj = this;
return {
get(target, propKey, receiver) {
if (typeof target[propKey as keyof object] === "function") {
const value = Reflect.get(target, propKey, receiver);

if (typeof value === "function") {
const fullPropKey = parentProp
? `${parentProp}.${String(propKey)}`
: String(propKey);
return new Proxy(target[propKey as keyof object], {
apply(applyTarget, thisArg, args) {
if ((thisArg.operations as string[]).includes(propKey as string))
obj.callFn(
propKey as string,
args.length === 1 ? args[0] : args,
);
obj.callFn(fullPropKey, args.length === 1 ? args[0] : args);
return Reflect.apply(applyTarget, thisArg, args);
},
});
}
return Reflect.get(target, propKey, receiver);

if (typeof value === "object" && value !== null && propKey === "acl") {
return new Proxy(
value,
obj.proxyDRPHandler(
parentProp ? `${parentProp}.${String(propKey)}` : String(propKey),
),
);
}

return value;
},
};
}
Expand Down Expand Up @@ -193,6 +205,28 @@ export class DRPObject implements IDRPObject {
}
}

private _applyOperation(drp: DRP, operation: Operation) {
const { type, value } = operation;

const typeParts = type.split(".");
// biome-ignore lint: target can be anything
let target: any = drp;
for (let i = 0; i < typeParts.length - 1; i++) {
target = target[typeParts[i]];
if (!target) {
throw new Error(`Invalid operation type: ${type}`);
}
}

const methodName = typeParts[typeParts.length - 1];
if (typeof target[methodName] !== "function") {
throw new Error(`${type} is not a function`);
}

const args = Array.isArray(value) ? value : [value];
target[methodName](...args);
}

private _computeDRP(
vertexDependencies: Hash[],
vertexOperation?: Operation | undefined,
Expand Down Expand Up @@ -224,14 +258,10 @@ export class DRPObject implements IDRPObject {
}

for (const op of linearizedOperations) {
const args = Array.isArray(op.value) ? op.value : [op.value];
drp[op.type](...args);
this._applyOperation(drp, op);
}
if (vertexOperation) {
const args = Array.isArray(vertexOperation.value)
? vertexOperation.value
: [vertexOperation.value];
drp[vertexOperation.type](...args);
this._applyOperation(drp, vertexOperation);
}

return drp;
Expand Down
68 changes: 67 additions & 1 deletion packages/object/tests/hashgraph.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AddWinsSetWithACL } from "@topology-foundation/blueprints/src/AddWinsSetWithACL/index.js";
import { beforeEach, describe, expect, test } from "vitest";
import { AddWinsSet } from "../../blueprints/src/AddWinsSet/index.js";
import { PseudoRandomWinsSet } from "../../blueprints/src/PseudoRandomWinsSet/index.js";
import { DRPObject, type Operation, OperationType } from "../src/index.js";

describe("HashGraph construction tests", () => {
Expand Down Expand Up @@ -633,3 +633,69 @@ describe("Vertex timestamp tests", () => {
).toThrowError("Invalid timestamp detected.");
});
});

describe("Operation with ACL tests", () => {
let obj1: DRPObject;
let obj2: DRPObject;

beforeEach(async () => {
const peerIdToPublicKey = new Map<string, string>([
["peer1", "publicKey1"],
]);
obj1 = new DRPObject(
"peer1",
new AddWinsSetWithACL<number>(peerIdToPublicKey),
);
obj2 = new DRPObject(
"peer2",
new AddWinsSetWithACL<number>(peerIdToPublicKey),
);
});

test("Node with admin permission can grant permission to other nodes", () => {
/*
ROOT -- V1:GRANT("peer2")
*/

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

drp1.acl.grant("peer1", "peer2", "publicKey2");
obj2.merge(obj1.hashGraph.getAllVertices());
expect(drp2.acl.isWriter("peer2")).toBe(true);
});

test("Node with writer permission can create vertices", () => {
/*
ROOT -- V1:GRANT("peer2") -- V2:ADD(1)
*/
const drp1 = obj1.drp as AddWinsSetWithACL<number>;
const drp2 = obj2.drp as AddWinsSetWithACL<number>;

drp1.acl.grant("peer1", "peer2", "publicKey2");
obj2.merge(obj1.hashGraph.getAllVertices());

drp2.add("peer2", 1);
obj1.merge(obj2.hashGraph.getAllVertices());
expect(drp1.contains(1)).toBe(true);
});

test("Revoke permission from writer", () => {
/*
ROOT -- V1:GRANT("peer2") -- V2:ADD(1) -- V3:REVOKE("peer2")
*/
const drp1 = obj1.drp as AddWinsSetWithACL<number>;
const drp2 = obj2.drp as AddWinsSetWithACL<number>;

drp1.acl.grant("peer1", "peer2", "publicKey2");
obj2.merge(obj1.hashGraph.getAllVertices());

expect(drp2.acl.isWriter("peer2")).toBe(true);
drp2.add("peer2", 1);

obj1.merge(obj2.hashGraph.getAllVertices());
drp1.acl.revoke("peer1", "peer2");
obj2.merge(obj1.hashGraph.getAllVertices());
expect(drp2.acl.isWriter("peer2")).toBe(false);
});
});
10 changes: 8 additions & 2 deletions pnpm-lock.yaml

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

0 comments on commit adfb540

Please sign in to comment.