Skip to content

Commit d1de58a

Browse files
committed
fix: err handling && jsdoc
1 parent 9c35430 commit d1de58a

File tree

2 files changed

+137
-129
lines changed

2 files changed

+137
-129
lines changed
Lines changed: 118 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import canonicalize from "canonicalize";
22
import {
3-
BadNextKeySpecifiedError,
4-
BadOptionsSpecifiedError,
5-
BadSignatureError,
6-
MalformedHashChainError,
7-
MalformedIndexChainError,
3+
BadNextKeySpecifiedError,
4+
BadOptionsSpecifiedError,
5+
BadSignatureError,
6+
MalformedHashChainError,
7+
MalformedIndexChainError,
88
} from "../errors/errors";
99
import { isSubsetOf } from "../utils/array";
1010
import { hash } from "../utils/hash";
1111
import {
12-
isGenesisOptions,
13-
isRotationOptions,
14-
type CreateLogEventOptions,
15-
type GenesisLogOptions,
16-
type LogEvent,
17-
type RotationLogOptions,
18-
type VerifierCallback,
12+
isGenesisOptions,
13+
isRotationOptions,
14+
type CreateLogEventOptions,
15+
type GenesisLogOptions,
16+
type LogEvent,
17+
type RotationLogOptions,
18+
type VerifierCallback,
1919
} from "./log.types";
2020
import type { StorageSpec } from "./storage/storage-spec";
2121

@@ -27,122 +27,122 @@ import type { StorageSpec } from "./storage/storage-spec";
2727
// TODO: Create a specification link inside our docs for how generation of identifier works
2828

2929
export class IDLogManager {
30-
repository: StorageSpec<LogEvent, LogEvent>;
30+
repository: StorageSpec<LogEvent, LogEvent>;
3131

32-
constructor(repository: StorageSpec<LogEvent, LogEvent>) {
33-
this.repository = repository;
34-
}
32+
constructor(repository: StorageSpec<LogEvent, LogEvent>) {
33+
this.repository = repository;
34+
}
3535

36-
static async validateLogChain(
37-
log: LogEvent[],
38-
verifyCallback: VerifierCallback,
39-
) {
40-
let currIndex = 0;
41-
let currentNextKeyHashesSeen: string[] = [];
42-
let lastUpdateKeysSeen: string[] = [];
43-
let lastHash: string | null = null;
36+
static async validateLogChain(
37+
log: LogEvent[],
38+
verifyCallback: VerifierCallback,
39+
) {
40+
let currIndex = 0;
41+
let currentNextKeyHashesSeen: string[] = [];
42+
let lastUpdateKeysSeen: string[] = [];
43+
let lastHash: string | null = null;
4444

45-
for (const e of log) {
46-
const [_index, _hash] = e.versionId.split("-");
47-
const index = Number(_index);
48-
if (currIndex !== index) throw new MalformedIndexChainError();
49-
const hashedUpdateKeys = await Promise.all(
50-
e.updateKeys.map(async (k) => await hash(k)),
51-
);
52-
if (index > 0) {
53-
const updateKeysSeen = isSubsetOf(
54-
hashedUpdateKeys,
55-
currentNextKeyHashesSeen,
56-
);
57-
if (!updateKeysSeen || lastHash !== _hash)
58-
throw new MalformedHashChainError();
59-
}
45+
for (const e of log) {
46+
const [_index, _hash] = e.versionId.split("-");
47+
const index = Number(_index);
48+
if (currIndex !== index) throw new MalformedIndexChainError();
49+
const hashedUpdateKeys = await Promise.all(
50+
e.updateKeys.map(async (k) => await hash(k)),
51+
);
52+
if (index > 0) {
53+
const updateKeysSeen = isSubsetOf(
54+
hashedUpdateKeys,
55+
currentNextKeyHashesSeen,
56+
);
57+
if (!updateKeysSeen || lastHash !== _hash)
58+
throw new MalformedHashChainError();
59+
}
6060

61-
currentNextKeyHashesSeen = e.nextKeyHashes;
62-
await IDLogManager.verifyLogEventProof(
63-
e,
64-
lastUpdateKeysSeen.length > 0 ? lastUpdateKeysSeen : e.updateKeys,
65-
verifyCallback,
66-
);
67-
lastUpdateKeysSeen = e.updateKeys;
68-
currIndex++;
69-
lastHash = await hash(canonicalize(e) as string);
70-
}
71-
return true;
72-
}
61+
currentNextKeyHashesSeen = e.nextKeyHashes;
62+
await IDLogManager.verifyLogEventProof(
63+
e,
64+
lastUpdateKeysSeen.length > 0 ? lastUpdateKeysSeen : e.updateKeys,
65+
verifyCallback,
66+
);
67+
lastUpdateKeysSeen = e.updateKeys;
68+
currIndex++;
69+
lastHash = await hash(canonicalize(e) as string);
70+
}
71+
return true;
72+
}
7373

74-
private static async verifyLogEventProof(
75-
e: LogEvent,
76-
currentUpdateKeys: string[],
77-
verifyCallback: VerifierCallback,
78-
) {
79-
const proof = e.proof;
80-
const copy = JSON.parse(JSON.stringify(e));
81-
// biome-ignore lint/performance/noDelete: we need to delete proof completely
82-
delete copy.proof;
83-
const canonicalJson = canonicalize(copy);
84-
let verified = false;
85-
if (!proof) throw new Error();
86-
for (const key of currentUpdateKeys) {
87-
const signValidates = await verifyCallback(
88-
canonicalJson as string,
89-
proof,
90-
key,
91-
);
92-
if (signValidates) verified = true;
93-
}
94-
if (!verified) throw new BadSignatureError();
95-
}
74+
private static async verifyLogEventProof(
75+
e: LogEvent,
76+
currentUpdateKeys: string[],
77+
verifyCallback: VerifierCallback,
78+
) {
79+
const proof = e.proof;
80+
const copy = JSON.parse(JSON.stringify(e));
81+
// biome-ignore lint/performance/noDelete: we need to delete proof completely
82+
delete copy.proof;
83+
const canonicalJson = canonicalize(copy);
84+
let verified = false;
85+
if (!proof) throw new BadSignatureError("No proof found in the log event.");
86+
for (const key of currentUpdateKeys) {
87+
const signValidates = await verifyCallback(
88+
canonicalJson as string,
89+
proof,
90+
key,
91+
);
92+
if (signValidates) verified = true;
93+
}
94+
if (!verified) throw new BadSignatureError();
95+
}
9696

97-
private async appendEntry(entries: LogEvent[], options: RotationLogOptions) {
98-
const { signer, nextKeyHashes, nextKeySigner } = options;
99-
const latestEntry = entries[entries.length - 1];
100-
const logHash = await hash(latestEntry);
101-
const index = Number(latestEntry.versionId.split("-")[0]) + 1;
97+
private async appendEntry(entries: LogEvent[], options: RotationLogOptions) {
98+
const { signer, nextKeyHashes, nextKeySigner } = options;
99+
const latestEntry = entries[entries.length - 1];
100+
const logHash = await hash(latestEntry);
101+
const index = Number(latestEntry.versionId.split("-")[0]) + 1;
102102

103-
const currKeyHash = await hash(nextKeySigner.pubKey);
104-
if (!latestEntry.nextKeyHashes.includes(currKeyHash))
105-
throw new BadNextKeySpecifiedError();
103+
const currKeyHash = await hash(nextKeySigner.pubKey);
104+
if (!latestEntry.nextKeyHashes.includes(currKeyHash))
105+
throw new BadNextKeySpecifiedError();
106106

107-
const logEvent: LogEvent = {
108-
id: latestEntry.id,
109-
versionTime: new Date(Date.now()),
110-
versionId: `${index}-${logHash}`,
111-
updateKeys: [nextKeySigner.pubKey],
112-
nextKeyHashes: nextKeyHashes,
113-
method: "w3id:v0.0.0",
114-
};
107+
const logEvent: LogEvent = {
108+
id: latestEntry.id,
109+
versionTime: new Date(Date.now()),
110+
versionId: `${index}-${logHash}`,
111+
updateKeys: [nextKeySigner.pubKey],
112+
nextKeyHashes: nextKeyHashes,
113+
method: "w3id:v0.0.0",
114+
};
115115

116-
const proof = await signer.sign(canonicalize(logEvent) as string);
117-
logEvent.proof = proof;
116+
const proof = await signer.sign(canonicalize(logEvent) as string);
117+
logEvent.proof = proof;
118118

119-
await this.repository.create(logEvent);
120-
return logEvent;
121-
}
119+
await this.repository.create(logEvent);
120+
return logEvent;
121+
}
122122

123-
private async createGenesisEntry(options: GenesisLogOptions) {
124-
const { id, nextKeyHashes, signer } = options;
125-
const logEvent: LogEvent = {
126-
id,
127-
versionId: `0-${id.split("@")[1]}`,
128-
versionTime: new Date(Date.now()),
129-
updateKeys: [signer.pubKey],
130-
nextKeyHashes: nextKeyHashes,
131-
method: "w3id:v0.0.0",
132-
};
133-
const proof = await signer.sign(canonicalize(logEvent) as string);
134-
logEvent.proof = proof;
135-
await this.repository.create(logEvent);
136-
return logEvent;
137-
}
123+
private async createGenesisEntry(options: GenesisLogOptions) {
124+
const { id, nextKeyHashes, signer } = options;
125+
const logEvent: LogEvent = {
126+
id,
127+
versionId: `0-${id.split("@")[1]}`,
128+
versionTime: new Date(Date.now()),
129+
updateKeys: [signer.pubKey],
130+
nextKeyHashes: nextKeyHashes,
131+
method: "w3id:v0.0.0",
132+
};
133+
const proof = await signer.sign(canonicalize(logEvent) as string);
134+
logEvent.proof = proof;
135+
await this.repository.create(logEvent);
136+
return logEvent;
137+
}
138138

139-
async createLogEvent(options: CreateLogEventOptions) {
140-
const entries = await this.repository.findMany({});
141-
if (entries.length > 0) {
142-
if (!isRotationOptions(options)) throw new BadOptionsSpecifiedError();
143-
return this.appendEntry(entries, options);
144-
}
145-
if (!isGenesisOptions(options)) throw new BadOptionsSpecifiedError();
146-
return this.createGenesisEntry(options);
147-
}
139+
async createLogEvent(options: CreateLogEventOptions) {
140+
const entries = await this.repository.findMany({});
141+
if (entries.length > 0) {
142+
if (!isRotationOptions(options)) throw new BadOptionsSpecifiedError();
143+
return this.appendEntry(entries, options);
144+
}
145+
if (!isGenesisOptions(options)) throw new BadOptionsSpecifiedError();
146+
return this.createGenesisEntry(options);
147+
}
148148
}
Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
/**
22
* Utility function to check if A is subset of B
3+
*
4+
* @param a Array to check if it's a subset
5+
* @param b Array to check against
6+
* @returns true if every element in 'a' is present in 'b' with at least the same frequency
7+
* @example
8+
* isSubsetOf([1, 2], [1, 2, 3]) // returns true
9+
* isSubsetOf([1, 1, 2], [1, 2, 3]) // returns false (not enough 1's in b)
10+
* isSubsetOf([], [1, 2]) // returns true (empty set is a subset of any set)
311
*/
412

513
export function isSubsetOf(a: unknown[], b: unknown[]) {
6-
const map = new Map();
14+
const map = new Map();
715

8-
for (const el of b) {
9-
map.set(el, (map.get(el) || 0) + 1);
10-
}
16+
for (const el of b) {
17+
map.set(el, (map.get(el) || 0) + 1);
18+
}
1119

12-
for (const el of a) {
13-
if (!map.has(el) || map.get(el) === 0) {
14-
return false;
15-
}
16-
map.set(el, map.get(el) - 1);
17-
}
20+
for (const el of a) {
21+
if (!map.has(el) || map.get(el) === 0) {
22+
return false;
23+
}
24+
map.set(el, map.get(el) - 1);
25+
}
1826

19-
return true;
27+
return true;
2028
}

0 commit comments

Comments
 (0)