Skip to content

Commit

Permalink
Add NTSchema's SegmentedObject
Browse files Browse the repository at this point in the history
  • Loading branch information
zjkmxy committed Feb 23, 2024
1 parent 9b976d8 commit 86a7a0a
Show file tree
Hide file tree
Showing 15 changed files with 688 additions and 44 deletions.
2 changes: 2 additions & 0 deletions deno.lock

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
"url": "https://github.com/UCLA-IRL/ndnts-aux/issues"
},
"dependencies": {
"event-iterator": "^2.0.0",
"eventemitter3": "^5.0.1",
"jose": "^5.2.1",
"tslib": "^2.6.2",
"type-fest": "^4.10.2",
"y-protocols": "^1.0.6",
"yjs": "^13.6.12"
},
Expand Down
8 changes: 6 additions & 2 deletions pnpm-lock.yaml

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

4 changes: 4 additions & 0 deletions src/namespace/base-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export class BaseNode {
public readonly onDetach = new EventChain<BaseNodeEvents['detach']>();
protected handler: NamespaceHandler | undefined = undefined;

constructor(public readonly describe?: string) {
this.describe ??= this.constructor.name;
}

public get namespaceHandler() {
return this.handler;
}
Expand Down
17 changes: 12 additions & 5 deletions src/namespace/expressing-point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface ExpressingPointEvents extends BaseNodeEvents {
}

export type ExpressingPointOpts = {
lifetimeMs: number;
lifetimeMs?: number;
interestSigner?: Signer;
canBePrefix?: boolean;
mustBeFresh?: boolean;
Expand All @@ -53,8 +53,9 @@ export class ExpressingPoint extends BaseNode {

constructor(
public readonly config: ExpressingPointOpts,
describe?: string,
) {
super();
super(describe);
}

public searchCache(target: schemaTree.StrictMatch<ExpressingPoint>, interest: Interest, deadline: number) {
Expand Down Expand Up @@ -134,8 +135,9 @@ export class ExpressingPoint extends BaseNode {
signer?: Signer;
lifetimeMs?: number;
deadline?: number;
verifier?: Verifier;
} = {},
): Promise<Data | undefined> {
): Promise<Data> {
// Construct Interest, but without signing, so the parameter digest is not there
const interestName = this.handler!.attachedPrefix!.append(...matched.name.comps);
const interestArgs = [interestName] as Array<Interest.CtorArg>;
Expand All @@ -156,6 +158,11 @@ export class ExpressingPoint extends BaseNode {
}
// TODO: FwHint is not supported for now. Who should provide this info?
const lifetimeMs = opts.lifetimeMs ?? this.config.lifetimeMs;
if (lifetimeMs === undefined) {
throw new Error(
`[${this.describe}:need] Unable to generate Interest when lifetimeMs is missing in config.`,
);
}
interestArgs.push(Interest.Lifetime(lifetimeMs));
const interest = new Interest(...interestArgs);

Expand Down Expand Up @@ -185,15 +192,15 @@ export class ExpressingPoint extends BaseNode {
// Express the Interest if not surpressed
const supressInterest = opts.supressInterest ?? this.config.supressInterest;
if (supressInterest) {
return undefined;
throw new Error(`Interest surpressed: ${interestName.toString()} @${this.describe}`);
}

const data = await this.handler!.endpoint!.consume(interest, {
// deno-lint-ignore no-explicit-any
signal: opts.abortSignal as any,
retx: this.config.retx,
// Note: the verifier is at the LeafNode if CanBePrefix is set
verifier: this.handler!.getVerifier(deadline),
verifier: opts.verifier ?? this.handler!.getVerifier(deadline),
});

// (no await) Save (cache) the data in the storage
Expand Down
23 changes: 16 additions & 7 deletions src/namespace/leaf-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export interface LeafNodeEvents extends ExpressingPointEvents {
}

export type LeafNodeOpts = ExpressingPointOpts & {
freshnessMs: number;
signer: Signer;
freshnessMs?: number;
signer?: Signer;
validityMs?: number;
contentType?: number;
};
Expand All @@ -29,8 +29,9 @@ export class LeafNode extends ExpressingPoint {

constructor(
public readonly config: LeafNodeOpts,
describe?: string,
) {
super(config);
super(config, describe);
}

public override async storeData(
Expand Down Expand Up @@ -58,21 +59,29 @@ export class LeafNode extends ExpressingPoint {
signer?: Signer;
finalBlockId?: Component;
} = {},
): Promise<Uint8Array> {
): Promise<Data> {
const payload = content instanceof Uint8Array ? content : new TextEncoder().encode(content);

const signer = opts.signer ?? this.config.signer;
const freshnessMs = opts.freshnessMs ?? this.config.freshnessMs;
if (!signer || freshnessMs === undefined) {
throw new Error(
`[${this.describe}:provide] Unable to generate Data when signer or freshnessMs is missing in config.`,
);
}

// Create Data
const dataName = this.handler!.attachedPrefix!.append(...matched.name.comps);
const data = new Data(
dataName,
Data.ContentType(this.config.contentType ?? 0), // Default is BLOB
Data.FreshnessPeriod(opts.freshnessMs ?? this.config.freshnessMs),
Data.FreshnessPeriod(freshnessMs),
payload,
);
if (opts.finalBlockId) {
data.finalBlockId = opts.finalBlockId;
}
await this.config.signer.sign(data);
await signer.sign(data);

const wire = Encoder.encode(data);
const validity = this.config.validityMs ?? 876000 * 3600000;
Expand All @@ -86,6 +95,6 @@ export class LeafNode extends ExpressingPoint {
validUntil,
});

return wire;
return data;
}
}
7 changes: 7 additions & 0 deletions src/namespace/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * as Pattern from './name-pattern.ts';
export * as Tree from './schema-tree.ts';
export { pattern } from './name-pattern.ts';
export * from './nt-schema.ts';
export * from './base-node.ts';
export * from './expressing-point.ts';
export * from './leaf-node.ts';
2 changes: 1 addition & 1 deletion src/namespace/name-pattern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export const makeStep = (
return pattern;
} else {
const value = mapping[pattern.tag];
if (!value) {
if (value === undefined) {
throw new Error(`The pattern variable ${pattern.tag} does not exist in the mapping.`);
}
const v = typeof value === 'number' ? Encoder.encode(NNI(value)) : value;
Expand Down
38 changes: 10 additions & 28 deletions src/namespace/nt-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ import { Ed25519, generateSigningKey } from '@ndn/keychain';
import { NtSchema, VerifyResult } from './nt-schema.ts';
import { InMemoryStorage } from '../storage/mod.ts';
import { LeafNode } from './leaf-node.ts';
import * as namePattern from './name-pattern.ts';
import * as Tree from './schema-tree.ts';
import { BaseNode } from './base-node.ts';

const { pattern } = namePattern;
export const b = ([value]: TemplateStringsArray) => new TextEncoder().encode(value);

Deno.test('NtSchema.1 Basic Interest and Data', async () => {
Expand All @@ -25,25 +22,20 @@ Deno.test('NtSchema.1 Basic Interest and Data', async () => {

// NTSchema side
const schema = new NtSchema();
const leaf = new LeafNode({
const leafNode = schema.set('/records/<8=recordId:string>', LeafNode, {
lifetimeMs: 100,
freshnessMs: 60000,
signer: digestSigning,
});
const leafNode = Tree.touch<BaseNode, LeafNode>(
schema.tree,
pattern`/records/<8=recordId:string>`,
leaf,
);
leaf.onVerify.addListener(async ({ pkt }) => {
leafNode.resource!.onVerify.addListener(async ({ pkt }) => {
try {
await digestSigning.verify(pkt);
return VerifyResult.Pass;
} catch {
return VerifyResult.Fail;
}
});
leaf.onInterest.addListener(async () => {
leafNode.resource!.onInterest.addListener(async () => {
const data3 = new Data(
name`${appPrefix}/records/8=rec3`,
Data.FreshnessPeriod(60000),
Expand Down Expand Up @@ -113,26 +105,21 @@ Deno.test('NtSchema.2 Data Storage', async () => {
// NTSchema side
const schema = new NtSchema();
const storageA = new InMemoryStorage();
const leaf = new LeafNode({
const leafNode = schema.set('/records/<8=recordId:string>', LeafNode, {
lifetimeMs: 100,
freshnessMs: 60000,
signer: digestSigning,
});
const leafNode = Tree.touch<BaseNode, LeafNode>(
schema.tree,
pattern`/records/<8=recordId:string>`,
leaf,
);
leaf.onVerify.addListener(async ({ pkt }) => {
leafNode.resource!.onVerify.addListener(async ({ pkt }) => {
try {
await digestSigning.verify(pkt);
return VerifyResult.Pass;
} catch {
return VerifyResult.Fail;
}
});
leaf.onSaveStorage.addListener(({ data, wire }) => storageA.set(data.name.toString(), wire));
leaf.onSearchStorage.addListener(async ({ interest }) => {
leafNode.resource!.onSaveStorage.addListener(({ data, wire }) => storageA.set(data.name.toString(), wire));
leafNode.resource!.onSearchStorage.addListener(async ({ interest }) => {
const wire = await storageA.get(interest.name.toString());
if (wire) {
return Decoder.decode(wire, Data);
Expand Down Expand Up @@ -200,17 +187,12 @@ Deno.test('NtSchema.3 Verification', async () => {

// NTSchema side
const schema = new NtSchema();
const leaf = new LeafNode({
const leafNode = schema.set('/records/<8=recordId:string>', LeafNode, {
lifetimeMs: 100,
freshnessMs: 60000,
signer: digestSigning,
});
const leafNode = Tree.touch<BaseNode, LeafNode>(
schema.tree,
pattern`/records/<8=recordId:string>`,
leaf,
);
leaf.onVerify.addListener(async ({ pkt, prevResult }) => {
leafNode.resource!.onVerify.addListener(async ({ pkt, prevResult }) => {
if (pkt.sigInfo?.type === SigType.Sha256) {
try {
await digestSigning.verify(pkt);
Expand All @@ -222,7 +204,7 @@ Deno.test('NtSchema.3 Verification', async () => {
return prevResult;
}
});
leaf.onVerify.addListener(async ({ pkt, prevResult }) => {
leafNode.resource!.onVerify.addListener(async ({ pkt, prevResult }) => {
if (pkt.sigInfo?.type === SigType.Ed25519) {
try {
await pubKey.verify(pkt);
Expand Down
17 changes: 16 additions & 1 deletion src/namespace/nt-schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Endpoint, Producer } from '@ndn/endpoint';
import { Data, Interest, Name, type Verifier } from '@ndn/packet';
// import * as namePattern from './name-pattern.ts';
import * as namePattern from './name-pattern.ts';
import * as schemaTree from './schema-tree.ts';
import { type BaseNode } from './base-node.ts';

Expand Down Expand Up @@ -94,6 +94,21 @@ export class NtSchema implements NamespaceHandler, AsyncDisposable {
this._attachedPrefix = undefined;
}

public set<Args extends Array<unknown>, T extends BaseNode>(
path: string | namePattern.Pattern,
klass: new (...args: Args) => T,
...args: Args
): schemaTree.Node<T> {
if (typeof path === 'string') {
path = namePattern.fromString(path);
}
return schemaTree.touch<BaseNode, T>(
this.tree,
path,
new klass(...args),
);
}

async [Symbol.asyncDispose]() {
if (this._producer) {
await this.detach();
Expand Down
Loading

0 comments on commit 86a7a0a

Please sign in to comment.