Skip to content

Commit 57b44bc

Browse files
committed
fix: w3id builder
1 parent dcd1b00 commit 57b44bc

File tree

4 files changed

+96
-6
lines changed

4 files changed

+96
-6
lines changed

infrastructure/w3id/src/index.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { generateRandomAlphaNum } from "./utils/rand";
1111
import { v4 as uuidv4 } from "uuid";
1212
import { generateUuid } from "./utils/uuid";
1313

14-
class W3ID {
14+
export class W3ID {
1515
constructor(
1616
public id: string,
1717
public logs?: IDLogManager,
@@ -23,6 +23,7 @@ export class W3IDBuilder {
2323
private repository?: StorageSpec<LogEvent, LogEvent>;
2424
private entropy?: string;
2525
private namespace?: string;
26+
private nextKeyHash?: string;
2627

2728
public withEntropy(str: string): W3IDBuilder {
2829
this.entropy = str;
@@ -46,7 +47,12 @@ export class W3IDBuilder {
4647
return this;
4748
}
4849

49-
public build(): W3ID {
50+
public withNextKeyHash(hash: string): W3IDBuilder {
51+
this.nextKeyHash = hash;
52+
return this;
53+
}
54+
55+
public async build(): Promise<W3ID> {
5056
this.entropy = this.entropy ?? generateRandomAlphaNum();
5157
this.namespace = this.namespace ?? uuidv4();
5258
const id = generateUuid(this.entropy, this.namespace);
@@ -57,7 +63,16 @@ export class W3IDBuilder {
5763
throw new Error(
5864
"Repository is required, pass with \`withRepository\` method",
5965
);
66+
67+
if (!this.nextKeyHash)
68+
throw new Error(
69+
"NextKeyHash is required pass with \`withNextKeyHash\` method",
70+
);
6071
const logs = new IDLogManager(this.repository, this.signer);
72+
await logs.createLogEvent({
73+
id,
74+
nextKeyHashes: [this.nextKeyHash],
75+
});
6176
return new W3ID(id, logs);
6277
}
6378
}

infrastructure/w3id/src/logs/log-manager.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,10 @@ export class IDLogManager {
132132

133133
private async createGenesisEntry(options: GenesisLogOptions) {
134134
const { id, nextKeyHashes } = options;
135+
const idTag = id.includes("@") ? id.split("@")[1] : id;
135136
const logEvent: LogEvent = {
136137
id,
137-
versionId: `0-${id.split("@")[1]}`,
138+
versionId: `0-${idTag}`,
138139
versionTime: new Date(Date.now()),
139140
updateKeys: [this.signer.pubKey],
140141
nextKeyHashes: nextKeyHashes,

infrastructure/w3id/tests/logs/log.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
MalformedIndexChainError,
2525
} from "../../src/errors/errors.ts";
2626

27-
class InMemoryStorage<T extends LogEvent, K extends LogEvent>
27+
export class InMemoryStorage<T extends LogEvent, K extends LogEvent>
2828
implements StorageSpec<T, K>
2929
{
3030
private data: K[] = [];
@@ -69,7 +69,7 @@ const signer = createSigner(keyPair);
6969
const logManager = new IDLogManager(InMemoryStorage.build(), signer);
7070
const w3id = `@${generateUuid("asdfa")}`;
7171

72-
const verifierCallback: VerifierCallback = async (
72+
export const verifierCallback: VerifierCallback = async (
7373
message: string,
7474
signature: string,
7575
pubKey: string,
@@ -86,7 +86,7 @@ const verifierCallback: VerifierCallback = async (
8686
return isValid;
8787
};
8888

89-
function createSigner(keyPair: nacl.SignKeyPair): Signer {
89+
export function createSigner(keyPair: nacl.SignKeyPair): Signer {
9090
const publicKey = uint8ArrayToHex(keyPair.publicKey);
9191
const signer: Signer = {
9292
pubKey: publicKey,
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { W3ID, W3IDBuilder } from "../src";
2+
import { describe, test, expect } from "vitest";
3+
import falso from "@ngneat/falso";
4+
import nacl from "tweetnacl";
5+
import {
6+
createSigner,
7+
InMemoryStorage,
8+
verifierCallback,
9+
} from "./logs/log.test";
10+
import { IDLogManager } from "../src/logs/log-manager";
11+
import { hash } from "../src/utils/hash";
12+
import { uint8ArrayToHex } from "../src/utils/codec";
13+
import { LogEvent } from "../src/logs/log.types";
14+
15+
const keyPair = nacl.sign.keyPair();
16+
17+
describe("W3IDBuilder", () => {
18+
test("ID Generation: Create Basic ID", async () => {
19+
const id = await new W3IDBuilder().build();
20+
expect(id).toBeInstanceOf(W3ID);
21+
expect(id.logs).toBeUndefined();
22+
});
23+
24+
test("ID Generation: UUID is Deterministic", async () => {
25+
const namespace = falso.randUuid();
26+
const entropy = falso.randText();
27+
28+
const id1 = await new W3IDBuilder()
29+
.withEntropy(entropy)
30+
.withNamespace(namespace)
31+
.build();
32+
33+
const id2 = await new W3IDBuilder()
34+
.withEntropy(entropy)
35+
.withNamespace(namespace)
36+
.build();
37+
expect(id1.id).toEqual(id2.id);
38+
expect(id1.logs).toBeUndefined();
39+
});
40+
41+
test("ID Generation: Creates IDLogManager", async () => {
42+
const id = await new W3IDBuilder()
43+
.withRepository(InMemoryStorage.build())
44+
.withSigner(createSigner(keyPair))
45+
.withNextKeyHash(falso.randText())
46+
.build();
47+
expect(id.logs).toBeInstanceOf(IDLogManager);
48+
const genesisLog = (await id.logs?.repository.findMany({}))[0];
49+
expect(genesisLog).toBeDefined();
50+
});
51+
52+
test("ID Mutation: Key Rotation Works", async () => {
53+
const nextKeyPair = nacl.sign.keyPair();
54+
const nextKeyHash = await hash(uint8ArrayToHex(nextKeyPair.publicKey));
55+
56+
const id = await new W3IDBuilder()
57+
.withRepository(InMemoryStorage.build())
58+
.withSigner(createSigner(keyPair))
59+
.withNextKeyHash(nextKeyHash)
60+
.build();
61+
62+
await id.logs?.createLogEvent({
63+
nextKeySigner: createSigner(nextKeyPair),
64+
nextKeyHashes: [falso.randText()],
65+
});
66+
67+
const logs = await id.logs?.repository.findMany({});
68+
const result = await IDLogManager.validateLogChain(
69+
logs as LogEvent[],
70+
verifierCallback,
71+
);
72+
expect(result).toBe(true);
73+
});
74+
});

0 commit comments

Comments
 (0)