@peerbit/log 4.0.4 → 4.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/clock.d.ts.map +1 -1
- package/dist/src/clock.js +20 -11
- package/dist/src/clock.js.map +1 -1
- package/dist/src/entry-create.d.ts +26 -0
- package/dist/src/entry-create.d.ts.map +1 -0
- package/dist/src/entry-create.js +5 -0
- package/dist/src/entry-create.js.map +1 -0
- package/dist/src/entry-index.d.ts +2 -1
- package/dist/src/entry-index.d.ts.map +1 -1
- package/dist/src/entry-index.js +2 -1
- package/dist/src/entry-index.js.map +1 -1
- package/dist/src/entry-shallow.d.ts +29 -0
- package/dist/src/entry-shallow.d.ts.map +1 -0
- package/dist/src/entry-shallow.js +75 -0
- package/dist/src/entry-shallow.js.map +1 -0
- package/dist/src/entry-type.d.ts +5 -0
- package/dist/src/entry-type.d.ts.map +1 -0
- package/dist/src/entry-type.js +6 -0
- package/dist/src/entry-type.js.map +1 -0
- package/dist/src/entry-v0.d.ts +121 -0
- package/dist/src/entry-v0.d.ts.map +1 -0
- package/dist/src/entry-v0.js +465 -0
- package/dist/src/entry-v0.js.map +1 -0
- package/dist/src/entry.d.ts +31 -153
- package/dist/src/entry.d.ts.map +1 -1
- package/dist/src/entry.js +24 -581
- package/dist/src/entry.js.map +1 -1
- package/dist/src/heads-cache.d.ts +1 -1
- package/dist/src/heads-cache.d.ts.map +1 -1
- package/dist/src/heads-cache.js +0 -1
- package/dist/src/heads-cache.js.map +1 -1
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/log.d.ts +6 -4
- package/dist/src/log.d.ts.map +1 -1
- package/dist/src/log.js +22 -82
- package/dist/src/log.js.map +1 -1
- package/dist/src/payload.d.ts +17 -0
- package/dist/src/payload.d.ts.map +1 -0
- package/dist/src/payload.js +53 -0
- package/dist/src/payload.js.map +1 -0
- package/dist/src/trim.d.ts +2 -1
- package/dist/src/trim.d.ts.map +1 -1
- package/dist/src/trim.js.map +1 -1
- package/package.json +3 -3
- package/src/clock.ts +3 -2
- package/src/entry-create.ts +30 -0
- package/src/entry-index.ts +3 -6
- package/src/entry-shallow.ts +61 -0
- package/src/entry-type.ts +4 -0
- package/src/entry-v0.ts +594 -0
- package/src/entry.ts +50 -693
- package/src/heads-cache.ts +1 -1
- package/src/index.ts +5 -0
- package/src/log.ts +23 -94
- package/src/payload.ts +44 -0
- package/src/trim.ts +2 -1
package/src/entry.ts
CHANGED
|
@@ -1,699 +1,77 @@
|
|
|
1
|
-
import {
|
|
2
|
-
deserialize,
|
|
3
|
-
field,
|
|
4
|
-
fixedArray,
|
|
5
|
-
option,
|
|
6
|
-
serialize,
|
|
7
|
-
variant,
|
|
8
|
-
vec,
|
|
9
|
-
} from "@dao-xyz/borsh";
|
|
1
|
+
import { deserialize, serialize } from "@dao-xyz/borsh";
|
|
10
2
|
import { type Blocks } from "@peerbit/blocks-interface";
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
SignatureWithKey,
|
|
19
|
-
X25519Keypair,
|
|
20
|
-
X25519PublicKey,
|
|
21
|
-
randomBytes,
|
|
22
|
-
sha256Base64,
|
|
23
|
-
} from "@peerbit/crypto";
|
|
24
|
-
import { verify } from "@peerbit/crypto";
|
|
25
|
-
import { id } from "@peerbit/indexer-interface";
|
|
26
|
-
import { type Keychain } from "@peerbit/keychain";
|
|
27
|
-
import { compare } from "uint8arrays";
|
|
28
|
-
import { LamportClock as Clock, HLC, Timestamp } from "./clock.js";
|
|
29
|
-
import { type Encoding, NO_ENCODING } from "./encoding.js";
|
|
30
|
-
import type { SortableEntry } from "./log-sorting.js";
|
|
31
|
-
import { logger } from "./logger.js";
|
|
32
|
-
import { equals } from "./utils.js";
|
|
33
|
-
|
|
34
|
-
export type MaybeEncryptionPublicKey =
|
|
35
|
-
| X25519PublicKey
|
|
36
|
-
| X25519PublicKey[]
|
|
37
|
-
| Ed25519PublicKey
|
|
38
|
-
| Ed25519PublicKey[]
|
|
39
|
-
| undefined;
|
|
40
|
-
|
|
41
|
-
const isMaybeEryptionPublicKey = (o: any) => {
|
|
42
|
-
if (!o) {
|
|
43
|
-
return true;
|
|
44
|
-
}
|
|
45
|
-
if (o instanceof X25519PublicKey || o instanceof Ed25519PublicKey) {
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
if (Array.isArray(o)) {
|
|
49
|
-
return true; // assume entries are either X25519PublicKey or Ed25519PublicKey
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export type EncryptionTemplateMaybeEncrypted = EntryEncryptionTemplate<
|
|
55
|
-
MaybeEncryptionPublicKey,
|
|
56
|
-
MaybeEncryptionPublicKey,
|
|
57
|
-
MaybeEncryptionPublicKey | { [key: string]: MaybeEncryptionPublicKey } // signature either all signature encrypted by same key, or each individually
|
|
58
|
-
>;
|
|
59
|
-
export interface EntryEncryption {
|
|
60
|
-
receiver: EncryptionTemplateMaybeEncrypted;
|
|
61
|
-
keypair: X25519Keypair;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function arrayToHex(arr: Uint8Array): string {
|
|
65
|
-
return [...new Uint8Array(arr)]
|
|
66
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
67
|
-
.join("");
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function toBufferLE(num: bigint, width: number): Uint8Array {
|
|
71
|
-
const hex = num.toString(16);
|
|
72
|
-
const padded = hex.padStart(width * 2, "0").slice(0, width * 2);
|
|
73
|
-
const arr = padded.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16));
|
|
74
|
-
if (!arr) {
|
|
75
|
-
throw new Error("Unexpected");
|
|
76
|
-
}
|
|
77
|
-
const buffer = Uint8Array.from(arr);
|
|
78
|
-
buffer.reverse();
|
|
79
|
-
return buffer;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function toBigIntLE(buf: Uint8Array): bigint {
|
|
83
|
-
const reversed = buf.reverse();
|
|
84
|
-
const hex = arrayToHex(reversed);
|
|
85
|
-
if (hex.length === 0) {
|
|
86
|
-
return BigInt(0);
|
|
87
|
-
}
|
|
88
|
-
return BigInt(`0x${hex}`);
|
|
89
|
-
}
|
|
3
|
+
import type { PublicSignKey, SignatureWithKey } from "@peerbit/crypto";
|
|
4
|
+
import type { Keychain } from "@peerbit/keychain";
|
|
5
|
+
import { LamportClock as Clock } from "./clock.js";
|
|
6
|
+
import type { Encoding } from "./encoding.js";
|
|
7
|
+
import type { ShallowEntry } from "./entry-shallow.js";
|
|
8
|
+
import type { EntryType } from "./entry-type.js";
|
|
9
|
+
import type { Payload } from "./payload.js";
|
|
90
10
|
|
|
91
11
|
export type CanAppend<T> = (canAppend: Entry<T>) => Promise<boolean> | boolean;
|
|
12
|
+
export type ShallowOrFullEntry<T> = ShallowEntry | Entry<T>;
|
|
92
13
|
|
|
93
|
-
|
|
94
|
-
export class Payload<T> {
|
|
95
|
-
@field({ type: Uint8Array })
|
|
96
|
-
data: Uint8Array;
|
|
97
|
-
|
|
98
|
-
encoding: Encoding<T>;
|
|
99
|
-
|
|
100
|
-
private _value?: T;
|
|
101
|
-
|
|
102
|
-
constructor(props: { data: Uint8Array; value?: T; encoding: Encoding<T> }) {
|
|
103
|
-
this.data = props.data;
|
|
104
|
-
this._value = props.value;
|
|
105
|
-
this.encoding = props?.encoding;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
equals(other: Payload<T>): boolean {
|
|
109
|
-
return equals(this.data, other.data);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
get isDecoded(): boolean {
|
|
113
|
-
return this._value != null;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
get value(): T {
|
|
117
|
-
if (this._value == null) {
|
|
118
|
-
throw new Error("Value not decoded. Invoke: .getValue once");
|
|
119
|
-
}
|
|
120
|
-
return this._value;
|
|
121
|
-
}
|
|
122
|
-
getValue(encoding: Encoding<T> = this.encoding || NO_ENCODING): T {
|
|
123
|
-
if (this._value !== undefined) {
|
|
124
|
-
return this._value;
|
|
125
|
-
}
|
|
126
|
-
return encoding.decoder(this.data);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export interface EntryEncryptionTemplate<A, B, C> {
|
|
131
|
-
meta: A;
|
|
132
|
-
payload: B;
|
|
133
|
-
signatures: C;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export enum EntryType {
|
|
137
|
-
APPEND = 0, // Add more data
|
|
138
|
-
CUT = 1, // Delete or Create tombstone ... delete all nexts, i
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/* @variant(0) */
|
|
142
|
-
export class Meta {
|
|
143
|
-
@field({ type: Clock })
|
|
14
|
+
interface Meta {
|
|
144
15
|
clock: Clock;
|
|
145
|
-
|
|
146
|
-
@field({ type: "string" })
|
|
147
16
|
gid: string; // graph id
|
|
148
|
-
|
|
149
|
-
@field({ type: vec("string") })
|
|
150
17
|
next: string[];
|
|
151
|
-
|
|
152
|
-
@field({ type: "u8" })
|
|
153
18
|
type: EntryType;
|
|
154
|
-
|
|
155
|
-
@field({ type: option(Uint8Array) })
|
|
156
|
-
data?: Uint8Array; // Optional metadata
|
|
157
|
-
|
|
158
|
-
constructor(properties: {
|
|
159
|
-
gid: string;
|
|
160
|
-
clock: Clock;
|
|
161
|
-
type: EntryType;
|
|
162
|
-
data?: Uint8Array;
|
|
163
|
-
next: string[];
|
|
164
|
-
}) {
|
|
165
|
-
this.gid = properties.gid;
|
|
166
|
-
this.clock = properties.clock;
|
|
167
|
-
this.type = properties.type;
|
|
168
|
-
this.data = properties.data;
|
|
169
|
-
this.next = properties.next;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
@variant(0)
|
|
174
|
-
export class Signatures {
|
|
175
|
-
@field({ type: vec(MaybeEncrypted) })
|
|
176
|
-
signatures!: MaybeEncrypted<SignatureWithKey>[];
|
|
177
|
-
|
|
178
|
-
constructor(properties?: { signatures: MaybeEncrypted<SignatureWithKey>[] }) {
|
|
179
|
-
if (properties) {
|
|
180
|
-
this.signatures = properties.signatures;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
equals(other: Signatures) {
|
|
185
|
-
if (this.signatures.length !== other.signatures.length) {
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
for (let i = 0; i < this.signatures.length; i++) {
|
|
189
|
-
if (!this.signatures[i].equals(other.signatures[i])) {
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return true;
|
|
194
|
-
}
|
|
19
|
+
data?: Uint8Array;
|
|
195
20
|
}
|
|
196
21
|
|
|
197
|
-
|
|
198
|
-
thing: Q,
|
|
199
|
-
keypair?: X25519Keypair,
|
|
200
|
-
receiver?: MaybeEncryptionPublicKey,
|
|
201
|
-
): Promise<MaybeEncrypted<Q>> | MaybeEncrypted<Q> => {
|
|
202
|
-
const receivers = receiver
|
|
203
|
-
? Array.isArray(receiver)
|
|
204
|
-
? receiver
|
|
205
|
-
: [receiver]
|
|
206
|
-
: undefined;
|
|
207
|
-
if (receivers?.length && receivers?.length > 0) {
|
|
208
|
-
if (!keypair) {
|
|
209
|
-
throw new Error("Keypair not provided");
|
|
210
|
-
}
|
|
211
|
-
return new DecryptedThing<Q>({
|
|
212
|
-
data: serialize(thing),
|
|
213
|
-
value: thing,
|
|
214
|
-
}).encrypt(keypair, receivers);
|
|
215
|
-
}
|
|
216
|
-
return new DecryptedThing<Q>({
|
|
217
|
-
data: serialize(thing),
|
|
218
|
-
value: thing,
|
|
219
|
-
});
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
export class ShallowEntry {
|
|
223
|
-
@id({ type: "string" })
|
|
224
|
-
hash: string;
|
|
225
|
-
|
|
226
|
-
@field({ type: Meta })
|
|
22
|
+
export interface Entry<T> {
|
|
227
23
|
meta: Meta;
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
@field({ type: "bool" })
|
|
233
|
-
head: boolean;
|
|
234
|
-
|
|
235
|
-
constructor(properties: {
|
|
236
|
-
hash: string;
|
|
237
|
-
meta: Meta;
|
|
238
|
-
payloadSize: number;
|
|
239
|
-
head: boolean;
|
|
240
|
-
}) {
|
|
241
|
-
this.hash = properties.hash;
|
|
242
|
-
this.meta = properties.meta;
|
|
243
|
-
this.payloadSize = properties.payloadSize;
|
|
244
|
-
this.head = properties.head;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
export type ShallowOrFullEntry<T> = ShallowEntry | Entry<T>;
|
|
248
|
-
|
|
249
|
-
@variant(0)
|
|
250
|
-
export class Entry<T>
|
|
251
|
-
implements EntryEncryptionTemplate<Meta, Payload<T>, SignatureWithKey[]>
|
|
252
|
-
{
|
|
253
|
-
@field({ type: MaybeEncrypted })
|
|
254
|
-
_meta: MaybeEncrypted<Meta>;
|
|
255
|
-
|
|
256
|
-
@field({ type: MaybeEncrypted })
|
|
257
|
-
_payload: MaybeEncrypted<Payload<T>>;
|
|
258
|
-
|
|
259
|
-
@field({ type: fixedArray("u8", 4) })
|
|
260
|
-
_reserved?: Uint8Array;
|
|
261
|
-
|
|
262
|
-
@field({ type: option(Signatures) })
|
|
263
|
-
_signatures?: Signatures;
|
|
264
|
-
|
|
265
|
-
@field({ type: option("string") }) // we do option because we serialize and store this in a block without the hash, to receive the hash, which we later set
|
|
266
|
-
hash!: string; // "zd...Foo", we'll set the hash after persisting the entry
|
|
267
|
-
|
|
24
|
+
payload: Payload<T>;
|
|
25
|
+
signatures: SignatureWithKey[];
|
|
26
|
+
hash: string;
|
|
27
|
+
size: number;
|
|
268
28
|
createdLocally?: boolean;
|
|
29
|
+
publicKeys: PublicSignKey[];
|
|
30
|
+
toShallow(isHead: boolean): ShallowEntry;
|
|
31
|
+
}
|
|
269
32
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
constructor(obj: {
|
|
274
|
-
payload: MaybeEncrypted<Payload<T>>;
|
|
275
|
-
signatures?: Signatures;
|
|
276
|
-
meta: MaybeEncrypted<Meta>;
|
|
277
|
-
reserved?: Uint8Array; // intentational type 0 (not used)h
|
|
278
|
-
hash?: string;
|
|
279
|
-
createdLocally?: boolean;
|
|
280
|
-
}) {
|
|
281
|
-
this._meta = obj.meta;
|
|
282
|
-
this._payload = obj.payload;
|
|
283
|
-
this._signatures = obj.signatures;
|
|
284
|
-
this._reserved = new Uint8Array([0, 0, 0, 0]);
|
|
285
|
-
this.createdLocally = obj.createdLocally;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
init(
|
|
33
|
+
export abstract class Entry<T> {
|
|
34
|
+
abstract init(
|
|
289
35
|
props:
|
|
290
36
|
| {
|
|
291
37
|
keychain?: Keychain;
|
|
292
38
|
encoding: Encoding<T>;
|
|
293
39
|
}
|
|
294
40
|
| Entry<T>,
|
|
295
|
-
): this
|
|
296
|
-
if (props instanceof Entry) {
|
|
297
|
-
this._keychain = props._keychain;
|
|
298
|
-
this._encoding = props._encoding;
|
|
299
|
-
} else {
|
|
300
|
-
this._keychain = props.keychain;
|
|
301
|
-
this._encoding = props.encoding;
|
|
302
|
-
}
|
|
303
|
-
return this;
|
|
304
|
-
}
|
|
41
|
+
): this;
|
|
305
42
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
return this._meta.decrypted.getValue(Meta);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
async getMeta(): Promise<Meta> {
|
|
318
|
-
await this._meta.decrypt(this._keychain);
|
|
319
|
-
return this.meta;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
async getClock(): Promise<Clock> {
|
|
323
|
-
return (await this.getMeta()).clock;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
get gid(): string {
|
|
327
|
-
return this._meta.decrypted.getValue(Meta).gid;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
async getGid(): Promise<string> {
|
|
331
|
-
return (await this.getMeta()).gid;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
get payload(): Payload<T> {
|
|
335
|
-
const payload = this._payload.decrypted.getValue(Payload);
|
|
336
|
-
payload.encoding = payload.encoding || this.encoding;
|
|
337
|
-
return payload;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
async getPayload(): Promise<Payload<T>> {
|
|
341
|
-
if (this._payload instanceof DecryptedThing) {
|
|
342
|
-
return this.payload;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
await this._payload.decrypt(this._keychain);
|
|
346
|
-
return this.payload;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
async getPayloadValue(): Promise<T> {
|
|
350
|
-
const payload = await this.getPayload();
|
|
351
|
-
return payload.isDecoded ? payload.value : payload.getValue(this.encoding);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
get publicKeys(): PublicSignKey[] {
|
|
355
|
-
return this.signatures.map((x) => x.publicKey);
|
|
356
|
-
}
|
|
43
|
+
abstract getMeta(): Promise<Meta> | Meta;
|
|
44
|
+
abstract getNext(): Promise<string[]> | string[];
|
|
45
|
+
abstract verifySignatures(): Promise<boolean> | boolean;
|
|
46
|
+
abstract getSignatures(): Promise<SignatureWithKey[]> | SignatureWithKey[];
|
|
47
|
+
abstract getClock(): Promise<Clock> | Clock;
|
|
48
|
+
abstract equals(other: Entry<T>): boolean;
|
|
49
|
+
abstract getPayloadValue(): Promise<T> | T;
|
|
50
|
+
abstract toSignable(): Entry<T>;
|
|
357
51
|
|
|
358
52
|
async getPublicKeys(): Promise<PublicSignKey[]> {
|
|
359
|
-
await this.getSignatures();
|
|
360
|
-
return
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
get next(): string[] {
|
|
364
|
-
return this.meta.next;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
async getNext(): Promise<string[]> {
|
|
368
|
-
return (await this.getMeta()).next;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
private _size!: number;
|
|
372
|
-
|
|
373
|
-
set size(number: number) {
|
|
374
|
-
this._size = number;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
get size(): number {
|
|
378
|
-
if (this._size == null) {
|
|
379
|
-
throw new Error(
|
|
380
|
-
"Size not set. Size is set when entry is, created, loaded or joined",
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
return this._size;
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Will only return signatures I can decrypt
|
|
387
|
-
* @returns signatures
|
|
388
|
-
*/
|
|
389
|
-
get signatures(): SignatureWithKey[] {
|
|
390
|
-
const signatures = this._signatures!.signatures.filter((x) => {
|
|
391
|
-
try {
|
|
392
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
393
|
-
x.decrypted;
|
|
394
|
-
return true;
|
|
395
|
-
} catch (error) {
|
|
396
|
-
return false;
|
|
397
|
-
}
|
|
398
|
-
}).map((x) => x.decrypted.getValue(SignatureWithKey));
|
|
399
|
-
if (signatures.length === 0) {
|
|
400
|
-
this._signatures?.signatures.forEach((x) => x.clear());
|
|
401
|
-
throw new Error("Failed to resolve any signature");
|
|
402
|
-
}
|
|
403
|
-
return signatures;
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* Will only return signatures I can decrypt
|
|
407
|
-
* @returns signatures
|
|
408
|
-
*/
|
|
409
|
-
async getSignatures(): Promise<SignatureWithKey[]> {
|
|
410
|
-
const results = await Promise.allSettled(
|
|
411
|
-
this._signatures!.signatures.map((x) => x.decrypt(this._keychain)),
|
|
412
|
-
);
|
|
413
|
-
|
|
414
|
-
if (logger.level === "debug" || logger.level === "trace") {
|
|
415
|
-
for (const [i, result] of results.entries()) {
|
|
416
|
-
if (result.status === "rejected") {
|
|
417
|
-
logger.debug("Failed to decrypt signature with index: " + i);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
return this.signatures;
|
|
53
|
+
const signatures = await this.getSignatures();
|
|
54
|
+
return signatures.map((s) => s.publicKey);
|
|
422
55
|
}
|
|
423
56
|
|
|
424
57
|
/**
|
|
425
|
-
*
|
|
426
|
-
* @
|
|
58
|
+
* Compares two entries.
|
|
59
|
+
* @param {Entry} a
|
|
60
|
+
* @param {Entry} b
|
|
61
|
+
* @returns {number} 1 if a is greater, -1 is b is greater
|
|
427
62
|
*/
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const signable = Entry.toSignable(this);
|
|
436
|
-
const signableBytes = serialize(signable);
|
|
437
|
-
for (const signature of signatures) {
|
|
438
|
-
if (!(await verify(signature, signableBytes))) {
|
|
439
|
-
return false;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
return true;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
static toSignable(entry: Entry<any>): Entry<any> {
|
|
446
|
-
// TODO fix types
|
|
447
|
-
const trimmed = new Entry({
|
|
448
|
-
meta: entry._meta,
|
|
449
|
-
payload: entry._payload,
|
|
450
|
-
reserved: entry._reserved,
|
|
451
|
-
signatures: undefined,
|
|
452
|
-
hash: undefined,
|
|
453
|
-
});
|
|
454
|
-
return trimmed;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
toSignable(): Entry<any> {
|
|
458
|
-
if (this._signatures) {
|
|
459
|
-
throw new Error("Expected signatures to be undefined");
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (this.hash) {
|
|
463
|
-
throw new Error("Expected hash to be undefined");
|
|
464
|
-
}
|
|
465
|
-
return Entry.toSignable(this);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
equals(other: Entry<T>) {
|
|
469
|
-
return (
|
|
470
|
-
equals(this._reserved, other._reserved) &&
|
|
471
|
-
this._meta.equals(other._meta) &&
|
|
472
|
-
this._signatures!.equals(other._signatures!) &&
|
|
473
|
-
this._payload.equals(other._payload)
|
|
474
|
-
); // dont compare hashes because the hash is a function of the other properties
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
async delete(store: Blocks): Promise<void> {
|
|
478
|
-
if (!this.hash) {
|
|
479
|
-
throw new Error("Missing hash");
|
|
480
|
-
}
|
|
481
|
-
await store.rm(this.hash);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
static createGid(seed?: Uint8Array): Promise<string> {
|
|
485
|
-
return sha256Base64(seed || randomBytes(32));
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
static async create<T>(properties: {
|
|
489
|
-
store: Blocks;
|
|
490
|
-
data: T;
|
|
491
|
-
meta?: {
|
|
492
|
-
clock?: Clock;
|
|
493
|
-
gid?: string;
|
|
494
|
-
type?: EntryType;
|
|
495
|
-
gidSeed?: Uint8Array;
|
|
496
|
-
data?: Uint8Array;
|
|
497
|
-
next?: SortableEntry[];
|
|
498
|
-
};
|
|
499
|
-
encoding?: Encoding<T>;
|
|
500
|
-
canAppend?: CanAppend<T>;
|
|
501
|
-
encryption?: EntryEncryption;
|
|
502
|
-
identity: Identity;
|
|
503
|
-
signers?: ((
|
|
504
|
-
data: Uint8Array,
|
|
505
|
-
) => Promise<SignatureWithKey> | SignatureWithKey)[];
|
|
506
|
-
}): Promise<Entry<T>> {
|
|
507
|
-
if (!properties.encoding || !properties?.meta?.next) {
|
|
508
|
-
properties = {
|
|
509
|
-
...properties,
|
|
510
|
-
meta: {
|
|
511
|
-
...properties?.meta,
|
|
512
|
-
next: properties.meta?.next ? properties.meta?.next : [],
|
|
513
|
-
},
|
|
514
|
-
encoding: properties.encoding ? properties.encoding : NO_ENCODING,
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
if (!properties.encoding) {
|
|
519
|
-
throw new Error("Missing encoding options");
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
if (properties.data == null) throw new Error("Entry requires data");
|
|
523
|
-
if (properties.meta?.next == null || !Array.isArray(properties.meta.next))
|
|
524
|
-
throw new Error("'next' argument is not an array");
|
|
525
|
-
|
|
526
|
-
// Clean the next objects and convert to hashes
|
|
527
|
-
const nexts = properties.meta?.next;
|
|
528
|
-
|
|
529
|
-
const payloadToSave = new Payload<T>({
|
|
530
|
-
data: properties.encoding.encoder(properties.data),
|
|
531
|
-
value: properties.data,
|
|
532
|
-
encoding: properties.encoding,
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
let clock: Clock | undefined = properties.meta?.clock;
|
|
536
|
-
if (!clock) {
|
|
537
|
-
const hlc = new HLC();
|
|
538
|
-
for (const next of nexts) {
|
|
539
|
-
hlc.update(next.meta.clock.timestamp);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
if (
|
|
543
|
-
properties.encryption?.receiver.signatures &&
|
|
544
|
-
properties.encryption?.receiver.meta
|
|
545
|
-
) {
|
|
546
|
-
throw new Error(
|
|
547
|
-
"Signature is to be encrypted yet the clock is not, which contains the publicKey as id. Either provide a custom Clock value that is not sensitive or set the receiver (encryption target) for the clock",
|
|
548
|
-
);
|
|
549
|
-
}
|
|
550
|
-
clock = new Clock({
|
|
551
|
-
id: properties.identity.publicKey.bytes,
|
|
552
|
-
timestamp: hlc.now(),
|
|
553
|
-
});
|
|
554
|
-
} else {
|
|
555
|
-
const cv = clock;
|
|
556
|
-
// check if nexts, that all nexts are happening BEFORE this clock value (else clock make no sense)
|
|
557
|
-
for (const n of nexts) {
|
|
558
|
-
if (Timestamp.compare(n.meta.clock.timestamp, cv.timestamp) >= 0) {
|
|
559
|
-
throw new Error(
|
|
560
|
-
"Expecting next(s) to happen before entry, got: " +
|
|
561
|
-
n.meta.clock.timestamp +
|
|
562
|
-
" > " +
|
|
563
|
-
cv.timestamp,
|
|
564
|
-
);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
const nextHashes: string[] = [];
|
|
570
|
-
let maxChainLength = 0n;
|
|
571
|
-
let gid: string | null = null;
|
|
572
|
-
if (nexts?.length > 0) {
|
|
573
|
-
// take min gid as our gid
|
|
574
|
-
if (properties.meta?.gid) {
|
|
575
|
-
throw new Error(
|
|
576
|
-
"Expecting '.meta.gid' property to be undefined if '.meta.next' is provided",
|
|
577
|
-
);
|
|
578
|
-
}
|
|
579
|
-
for (const n of nexts) {
|
|
580
|
-
if (!n.hash) {
|
|
581
|
-
throw new Error("Expecting hash to be defined to next entries");
|
|
582
|
-
}
|
|
583
|
-
nextHashes.push(n.hash);
|
|
584
|
-
gid =
|
|
585
|
-
gid == null
|
|
586
|
-
? n.meta.gid
|
|
587
|
-
: n.meta.gid < (gid as string)
|
|
588
|
-
? n.meta.gid
|
|
589
|
-
: gid;
|
|
590
|
-
}
|
|
591
|
-
} else {
|
|
592
|
-
gid =
|
|
593
|
-
properties.meta?.gid ||
|
|
594
|
-
(await Entry.createGid(properties.meta?.gidSeed));
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
maxChainLength += 1n; // include this
|
|
598
|
-
|
|
599
|
-
const metadataEncrypted = await maybeEncrypt(
|
|
600
|
-
new Meta({
|
|
601
|
-
clock,
|
|
602
|
-
gid: gid!,
|
|
603
|
-
type: properties.meta?.type ?? EntryType.APPEND,
|
|
604
|
-
data: properties.meta?.data,
|
|
605
|
-
next: nextHashes,
|
|
606
|
-
}),
|
|
607
|
-
properties.encryption?.keypair,
|
|
608
|
-
properties.encryption?.receiver.meta,
|
|
609
|
-
);
|
|
610
|
-
|
|
611
|
-
const payload = await maybeEncrypt(
|
|
612
|
-
payloadToSave,
|
|
613
|
-
properties.encryption?.keypair,
|
|
614
|
-
properties.encryption?.receiver.payload,
|
|
615
|
-
);
|
|
616
|
-
|
|
617
|
-
// Sign id, encrypted payload, clock, nexts, refs
|
|
618
|
-
const entry: Entry<T> = new Entry<T>({
|
|
619
|
-
meta: metadataEncrypted,
|
|
620
|
-
payload,
|
|
621
|
-
signatures: undefined,
|
|
622
|
-
createdLocally: true,
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
const signers = properties.signers || [
|
|
626
|
-
properties.identity.sign.bind(properties.identity),
|
|
627
|
-
];
|
|
628
|
-
const signable = entry.toSignable();
|
|
629
|
-
const signableBytes = serialize(signable);
|
|
630
|
-
let signatures = await Promise.all(
|
|
631
|
-
signers.map((signer) => signer(signableBytes)),
|
|
632
|
-
);
|
|
633
|
-
signatures = signatures.sort((a, b) => compare(a.signature, b.signature));
|
|
634
|
-
|
|
635
|
-
const encryptedSignatures: MaybeEncrypted<SignatureWithKey>[] = [];
|
|
636
|
-
const encryptAllSignaturesWithSameKey = isMaybeEryptionPublicKey(
|
|
637
|
-
properties.encryption?.receiver?.signatures,
|
|
638
|
-
);
|
|
639
|
-
|
|
640
|
-
for (const signature of signatures) {
|
|
641
|
-
const encryptionRecievers = encryptAllSignaturesWithSameKey
|
|
642
|
-
? properties.encryption?.receiver?.signatures
|
|
643
|
-
: (properties.encryption?.receiver?.signatures as any)?.[
|
|
644
|
-
signature.publicKey.hashcode()
|
|
645
|
-
]; // TODO types
|
|
646
|
-
const signatureEncrypted = await maybeEncrypt(
|
|
647
|
-
signature,
|
|
648
|
-
properties.encryption?.keypair,
|
|
649
|
-
encryptionRecievers,
|
|
650
|
-
);
|
|
651
|
-
encryptedSignatures.push(signatureEncrypted);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
entry._signatures = new Signatures({
|
|
655
|
-
signatures: encryptedSignatures,
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
if (properties.canAppend && !(await properties.canAppend(entry))) {
|
|
659
|
-
throw new AccessError();
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
// Append hash
|
|
663
|
-
entry.hash = await Entry.toMultihash(properties.store, entry);
|
|
664
|
-
|
|
665
|
-
entry.init({ encoding: properties.encoding });
|
|
666
|
-
|
|
667
|
-
return entry;
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
get payloadByteLength() {
|
|
671
|
-
return this._payload.byteLength;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
toShallow(isHead: boolean): ShallowEntry {
|
|
675
|
-
return new ShallowEntry({
|
|
676
|
-
hash: this.hash,
|
|
677
|
-
payloadSize: this._payload.byteLength,
|
|
678
|
-
head: isHead,
|
|
679
|
-
meta: new Meta({
|
|
680
|
-
gid: this.meta.gid,
|
|
681
|
-
data: this.meta.data,
|
|
682
|
-
clock: this.meta.clock,
|
|
683
|
-
next: this.meta.next,
|
|
684
|
-
type: this.meta.type,
|
|
685
|
-
}),
|
|
686
|
-
});
|
|
63
|
+
static compare<T>(a: Entry<T>, b: Entry<T>) {
|
|
64
|
+
const aClock = a.meta.clock;
|
|
65
|
+
const bClock = b.meta.clock;
|
|
66
|
+
const distance = Clock.compare(aClock, bClock);
|
|
67
|
+
if (distance === 0) return aClock.id < bClock.id ? -1 : 1;
|
|
68
|
+
return distance;
|
|
687
69
|
}
|
|
688
70
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
* console.log(multihash)
|
|
694
|
-
* // "Qm...Foo"
|
|
695
|
-
*/
|
|
696
|
-
static async toMultihash<T>(store: Blocks, entry: Entry<T>): Promise<string> {
|
|
71
|
+
static toMultihash<T>(
|
|
72
|
+
store: Blocks,
|
|
73
|
+
entry: Entry<T>,
|
|
74
|
+
): Promise<string> | string {
|
|
697
75
|
if (entry.hash) {
|
|
698
76
|
throw new Error("Expected hash to be missing");
|
|
699
77
|
}
|
|
@@ -703,18 +81,11 @@ export class Entry<T>
|
|
|
703
81
|
return store.put(bytes);
|
|
704
82
|
}
|
|
705
83
|
|
|
706
|
-
|
|
707
|
-
* Create an Entry from a hash.
|
|
708
|
-
* @example
|
|
709
|
-
* const entry = await Entry.fromMultihash(store, "zd...Foo")
|
|
710
|
-
* console.log(entry)
|
|
711
|
-
* // { hash: "Zd...Foo", payload: "hello", next: [] }
|
|
712
|
-
*/
|
|
713
|
-
static async fromMultihash<T>(
|
|
84
|
+
static fromMultihash = async <T>(
|
|
714
85
|
store: Blocks,
|
|
715
86
|
hash: string,
|
|
716
87
|
options?: { timeout?: number; replicate?: boolean },
|
|
717
|
-
) {
|
|
88
|
+
) => {
|
|
718
89
|
if (!hash) throw new Error(`Invalid hash: ${hash}`);
|
|
719
90
|
const bytes = await store.get(hash, options);
|
|
720
91
|
if (!bytes) {
|
|
@@ -724,21 +95,7 @@ export class Entry<T>
|
|
|
724
95
|
entry.hash = hash;
|
|
725
96
|
entry.size = bytes.length;
|
|
726
97
|
return entry as Entry<T>;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
/**
|
|
730
|
-
* Compares two entries.
|
|
731
|
-
* @param {Entry} a
|
|
732
|
-
* @param {Entry} b
|
|
733
|
-
* @returns {number} 1 if a is greater, -1 is b is greater
|
|
734
|
-
*/
|
|
735
|
-
static compare<T>(a: Entry<T>, b: Entry<T>) {
|
|
736
|
-
const aClock = a.meta.clock;
|
|
737
|
-
const bClock = b.meta.clock;
|
|
738
|
-
const distance = Clock.compare(aClock, bClock);
|
|
739
|
-
if (distance === 0) return aClock.id < bClock.id ? -1 : 1;
|
|
740
|
-
return distance;
|
|
741
|
-
}
|
|
98
|
+
};
|
|
742
99
|
|
|
743
100
|
/**
|
|
744
101
|
* Check if an entry equals another entry.
|
|
@@ -757,7 +114,7 @@ export class Entry<T>
|
|
|
757
114
|
* @returns {boolean}
|
|
758
115
|
*/
|
|
759
116
|
static isDirectParent<T>(entry1: Entry<T>, entry2: Entry<T>) {
|
|
760
|
-
return entry2.next.includes(entry1.hash as any); // TODO fix types
|
|
117
|
+
return entry2.meta.next.includes(entry1.hash as any); // TODO fix types
|
|
761
118
|
}
|
|
762
119
|
|
|
763
120
|
/**
|