@peerbit/log 6.1.1 → 6.2.0
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/benchmark/native-graph.js +447 -5
- package/dist/benchmark/native-graph.js.map +1 -1
- package/dist/src/clock.d.ts +1 -0
- package/dist/src/clock.d.ts.map +1 -1
- package/dist/src/clock.js +32 -0
- package/dist/src/clock.js.map +1 -1
- package/dist/src/entry-create.d.ts +1 -0
- package/dist/src/entry-create.d.ts.map +1 -1
- package/dist/src/entry-create.js.map +1 -1
- package/dist/src/entry-index.d.ts +288 -12
- package/dist/src/entry-index.d.ts.map +1 -1
- package/dist/src/entry-index.js +1478 -33
- package/dist/src/entry-index.js.map +1 -1
- package/dist/src/entry-v0.d.ts +184 -1
- package/dist/src/entry-v0.d.ts.map +1 -1
- package/dist/src/entry-v0.js +1021 -12
- package/dist/src/entry-v0.js.map +1 -1
- package/dist/src/entry.d.ts +73 -1
- package/dist/src/entry.d.ts.map +1 -1
- package/dist/src/entry.js +124 -1
- package/dist/src/entry.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/log.d.ts +62 -13
- package/dist/src/log.d.ts.map +1 -1
- package/dist/src/log.js +2282 -100
- package/dist/src/log.js.map +1 -1
- package/dist/src/runtime.d.ts +2 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +10 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/trim.d.ts +14 -2
- package/dist/src/trim.d.ts.map +1 -1
- package/dist/src/trim.js +92 -4
- package/dist/src/trim.js.map +1 -1
- package/package.json +18 -15
- package/src/clock.ts +34 -0
- package/src/entry-create.ts +1 -0
- package/src/entry-index.ts +2232 -68
- package/src/entry-v0.ts +1488 -13
- package/src/entry.ts +235 -1
- package/src/index.ts +7 -1
- package/src/log.ts +3246 -206
- package/src/runtime.ts +16 -0
- package/src/trim.ts +133 -7
package/src/entry-v0.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
deserialize,
|
|
2
3
|
field,
|
|
3
4
|
fixedArray,
|
|
4
5
|
option,
|
|
@@ -6,10 +7,11 @@ import {
|
|
|
6
7
|
variant,
|
|
7
8
|
vec,
|
|
8
9
|
} from "@dao-xyz/borsh";
|
|
9
|
-
import { type Blocks } from "@peerbit/blocks-interface";
|
|
10
|
+
import { type Blocks, calculateRawCid } from "@peerbit/blocks-interface";
|
|
10
11
|
import {
|
|
11
12
|
AccessError,
|
|
12
13
|
DecryptedThing,
|
|
14
|
+
Ed25519Keypair,
|
|
13
15
|
Ed25519PublicKey,
|
|
14
16
|
type Identity,
|
|
15
17
|
MaybeEncrypted,
|
|
@@ -28,14 +30,499 @@ import { LamportClock as Clock, HLC, Timestamp } from "./clock.js";
|
|
|
28
30
|
import { type Encoding, NO_ENCODING } from "./encoding.js";
|
|
29
31
|
import { ShallowEntry, ShallowMeta } from "./entry-shallow.js";
|
|
30
32
|
import { EntryType } from "./entry-type.js";
|
|
31
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
type CanAppend,
|
|
35
|
+
Entry,
|
|
36
|
+
type PreparedAppendChain,
|
|
37
|
+
type PreparedAppendCommitOnlyChain,
|
|
38
|
+
type PreparedNativeLogEntry,
|
|
39
|
+
} from "./entry.js";
|
|
32
40
|
import type { SortableEntry } from "./log-sorting.js";
|
|
33
41
|
import { logger as baseLogger } from "./logger.js";
|
|
34
42
|
import { Payload } from "./payload.js";
|
|
43
|
+
import { canUseOptionalNativeModuleImports } from "./runtime.js";
|
|
35
44
|
import { equals } from "./utils.js";
|
|
36
45
|
|
|
37
46
|
const log = baseLogger.newScope("entry-v0");
|
|
38
47
|
const traceLogger = log.trace as typeof log & { enabled?: boolean };
|
|
48
|
+
const RANDOM_GID_BYTES = 32;
|
|
49
|
+
const RANDOM_GID_POOL_SIZE = 64;
|
|
50
|
+
const NATIVE_RAW_CID_BATCH_MIN_BYTES = 1 << 20;
|
|
51
|
+
let randomGidPool: string[] = [];
|
|
52
|
+
|
|
53
|
+
const createRandomGid = (): string => {
|
|
54
|
+
if (randomGidPool.length === 0) {
|
|
55
|
+
const bytes = randomBytes(RANDOM_GID_BYTES * RANDOM_GID_POOL_SIZE);
|
|
56
|
+
randomGidPool = new Array<string>(RANDOM_GID_POOL_SIZE);
|
|
57
|
+
for (let i = 0; i < RANDOM_GID_POOL_SIZE; i++) {
|
|
58
|
+
const offset = i * RANDOM_GID_BYTES;
|
|
59
|
+
randomGidPool[i] = toBase64(
|
|
60
|
+
bytes.subarray(offset, offset + RANDOM_GID_BYTES),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return randomGidPool.pop()!;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
type NativePlainChainInput = {
|
|
68
|
+
clockId: Uint8Array;
|
|
69
|
+
privateKey: Uint8Array;
|
|
70
|
+
publicKey: Uint8Array;
|
|
71
|
+
wallTimes: Array<bigint | number | string>;
|
|
72
|
+
logicals?: number[];
|
|
73
|
+
gid: string;
|
|
74
|
+
initialNext?: string[];
|
|
75
|
+
type?: number;
|
|
76
|
+
metaDatas?: Array<Uint8Array | undefined>;
|
|
77
|
+
payloadDatas: Uint8Array[];
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
type NativePlainEntryInput = {
|
|
81
|
+
clockId: Uint8Array;
|
|
82
|
+
privateKey: Uint8Array;
|
|
83
|
+
publicKey: Uint8Array;
|
|
84
|
+
wallTime: bigint | number | string;
|
|
85
|
+
logical?: number;
|
|
86
|
+
gid: string;
|
|
87
|
+
next?: string[];
|
|
88
|
+
type?: number;
|
|
89
|
+
metaData?: Uint8Array;
|
|
90
|
+
payloadData: Uint8Array;
|
|
91
|
+
includeMaterializationBytes?: boolean;
|
|
92
|
+
includeAppendFactsBytes?: boolean;
|
|
93
|
+
trimLengthTo?: number;
|
|
94
|
+
resolveTrimmedEntries?: boolean;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
type NativePlainEntriesInput = {
|
|
98
|
+
clockId: Uint8Array;
|
|
99
|
+
privateKey: Uint8Array;
|
|
100
|
+
publicKey: Uint8Array;
|
|
101
|
+
wallTimes: Array<bigint | number | string>;
|
|
102
|
+
logicals?: number[];
|
|
103
|
+
gids: string[];
|
|
104
|
+
nexts?: string[][];
|
|
105
|
+
type?: number;
|
|
106
|
+
metaDatas?: Array<Uint8Array | undefined>;
|
|
107
|
+
payloadDatas: Uint8Array[];
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
type NativePreparedPlainEntry = {
|
|
111
|
+
bytes?: Uint8Array;
|
|
112
|
+
cid: string;
|
|
113
|
+
byteLength: number;
|
|
114
|
+
signature?: Uint8Array;
|
|
115
|
+
next: string[];
|
|
116
|
+
metaBytes?: Uint8Array;
|
|
117
|
+
payloadBytes?: Uint8Array;
|
|
118
|
+
signatureBytes?: Uint8Array;
|
|
119
|
+
hashDigestBytes?: Uint8Array;
|
|
120
|
+
trimmedEntries?: PreparedNativeLogEntry[];
|
|
121
|
+
trimmedEntryHashes?: string[];
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export type PreparedRawEntryV0Facts = {
|
|
125
|
+
cid: string;
|
|
126
|
+
hashDigestBytes: Uint8Array;
|
|
127
|
+
byteLength: number;
|
|
128
|
+
clockId: Uint8Array;
|
|
129
|
+
wallTime: bigint;
|
|
130
|
+
logical: number;
|
|
131
|
+
gid: string;
|
|
132
|
+
next: string[];
|
|
133
|
+
type: EntryType;
|
|
134
|
+
metaBytes: Uint8Array;
|
|
135
|
+
metaData?: Uint8Array;
|
|
136
|
+
payloadByteLength: number;
|
|
137
|
+
signatureVerified: boolean;
|
|
138
|
+
requestedReplicas?: number;
|
|
139
|
+
hashNumber?: string;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
143
|
+
|
|
144
|
+
type PlainAppendChainCommitOnlyProperties<T> = {
|
|
145
|
+
data: T[];
|
|
146
|
+
payloadDatas?: Uint8Array[];
|
|
147
|
+
meta?: {
|
|
148
|
+
clocks: () => Clock[];
|
|
149
|
+
gid?: string;
|
|
150
|
+
type?: EntryType;
|
|
151
|
+
gidSeed?: Uint8Array;
|
|
152
|
+
data?: Uint8Array;
|
|
153
|
+
next?: SortableEntry[];
|
|
154
|
+
};
|
|
155
|
+
encoding: Encoding<T>;
|
|
156
|
+
identity: Identity;
|
|
157
|
+
deferStore: boolean;
|
|
158
|
+
nativeGraph?: NativeEntryV0Graph;
|
|
159
|
+
nativeBlockStore?: unknown;
|
|
160
|
+
includeMaterializationBytes?: boolean;
|
|
161
|
+
includeAppendFactsBytes?: boolean;
|
|
162
|
+
nativeTrimLengthTo?: number;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
type NativeEntryV0Graph = {
|
|
166
|
+
prepareEntryV0PlainChainAndPut?(
|
|
167
|
+
input: NativePlainChainInput,
|
|
168
|
+
): MaybePromise<NativePreparedPlainEntry[]>;
|
|
169
|
+
prepareEntryV0PlainEntryAndPut?(
|
|
170
|
+
input: NativePlainEntryInput,
|
|
171
|
+
): MaybePromise<NativePreparedPlainEntry>;
|
|
172
|
+
prepareEntryV0PlainChainCommit?(
|
|
173
|
+
input: NativePlainChainInput,
|
|
174
|
+
blockStore: unknown,
|
|
175
|
+
): MaybePromise<NativePreparedPlainEntry[] | undefined>;
|
|
176
|
+
prepareEntryV0PlainEntryCommit?(
|
|
177
|
+
input: NativePlainEntryInput,
|
|
178
|
+
blockStore: unknown,
|
|
179
|
+
): MaybePromise<NativePreparedPlainEntry | undefined>;
|
|
180
|
+
prepareEntryV0PlainEntriesCommit?(
|
|
181
|
+
input: NativePlainEntriesInput,
|
|
182
|
+
blockStore: unknown,
|
|
183
|
+
): MaybePromise<NativePreparedPlainEntry[] | undefined>;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
type NativeEntryV0Encoder = {
|
|
187
|
+
encodeEntryV0Signable(input: {
|
|
188
|
+
clockId: Uint8Array;
|
|
189
|
+
wallTime: bigint;
|
|
190
|
+
logical?: number;
|
|
191
|
+
gid: string;
|
|
192
|
+
next?: string[];
|
|
193
|
+
type?: number;
|
|
194
|
+
metaData?: Uint8Array;
|
|
195
|
+
payloadData: Uint8Array;
|
|
196
|
+
}): Promise<Uint8Array>;
|
|
197
|
+
encodeEntryV0Storage(input: {
|
|
198
|
+
clockId: Uint8Array;
|
|
199
|
+
wallTime: bigint;
|
|
200
|
+
logical?: number;
|
|
201
|
+
gid: string;
|
|
202
|
+
next?: string[];
|
|
203
|
+
type?: number;
|
|
204
|
+
metaData?: Uint8Array;
|
|
205
|
+
payloadData: Uint8Array;
|
|
206
|
+
signature: Uint8Array;
|
|
207
|
+
signaturePublicKey: Uint8Array;
|
|
208
|
+
prehash?: number;
|
|
209
|
+
}): Promise<Uint8Array>;
|
|
210
|
+
encodeEntryV0StorageWithCid?(input: {
|
|
211
|
+
clockId: Uint8Array;
|
|
212
|
+
wallTime: bigint;
|
|
213
|
+
logical?: number;
|
|
214
|
+
gid: string;
|
|
215
|
+
next?: string[];
|
|
216
|
+
type?: number;
|
|
217
|
+
metaData?: Uint8Array;
|
|
218
|
+
payloadData: Uint8Array;
|
|
219
|
+
signature: Uint8Array;
|
|
220
|
+
signaturePublicKey: Uint8Array;
|
|
221
|
+
prehash?: number;
|
|
222
|
+
}): Promise<{ bytes: Uint8Array; cid: string }>;
|
|
223
|
+
prepareEntryV0PlainChain?(
|
|
224
|
+
input: NativePlainChainInput,
|
|
225
|
+
): Promise<NativePreparedPlainEntry[]>;
|
|
226
|
+
prepareEntryV0PlainEntry?(
|
|
227
|
+
input: NativePlainEntryInput,
|
|
228
|
+
): Promise<NativePreparedPlainEntry>;
|
|
229
|
+
calculateRawCidV1(bytes: Uint8Array): Promise<string>;
|
|
230
|
+
calculateRawCidV1Batch?(blocks: Uint8Array[]): Promise<string[]>;
|
|
231
|
+
entryV0PlainPayloadDataFromStorage?(
|
|
232
|
+
bytes: Uint8Array,
|
|
233
|
+
): Promise<Uint8Array>;
|
|
234
|
+
prepareRawEntryV0Batch?(
|
|
235
|
+
blocks: Uint8Array[],
|
|
236
|
+
): Promise<PreparedRawEntryV0Facts[]>;
|
|
237
|
+
verifyEd25519Batch?(
|
|
238
|
+
inputs: Ed25519VerifyBatchInput[],
|
|
239
|
+
): Promise<boolean[]>;
|
|
240
|
+
verifyEntryV0Ed25519Batch?(
|
|
241
|
+
inputs: EntryV0Ed25519VerifyInput[],
|
|
242
|
+
): Promise<boolean[]>;
|
|
243
|
+
verifyEntryV0Ed25519StorageBatch?(
|
|
244
|
+
blocks: Uint8Array[],
|
|
245
|
+
): Promise<boolean[]>;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
let nativeEntryV0EncoderPromise:
|
|
249
|
+
| Promise<NativeEntryV0Encoder | undefined>
|
|
250
|
+
| undefined;
|
|
251
|
+
let nativeEntryV0EncoderLoaded = false;
|
|
252
|
+
let nativeEntryV0Encoder: NativeEntryV0Encoder | undefined;
|
|
253
|
+
|
|
254
|
+
const isPromiseLike = <T>(value: MaybePromise<T>): value is Promise<T> =>
|
|
255
|
+
!!value && typeof (value as Promise<T>).then === "function";
|
|
256
|
+
|
|
257
|
+
const getSynchronousBlockBytes = (
|
|
258
|
+
store: unknown,
|
|
259
|
+
cid: string,
|
|
260
|
+
): Uint8Array | undefined => {
|
|
261
|
+
const value = (
|
|
262
|
+
store as { get?: (cid: string) => unknown } | undefined
|
|
263
|
+
)?.get?.(cid);
|
|
264
|
+
return value instanceof Uint8Array ? value : undefined;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const nativeEntryV0EncoderFromModule = (mod: {
|
|
268
|
+
encodeEntryV0Signable?: NativeEntryV0Encoder["encodeEntryV0Signable"];
|
|
269
|
+
encodeEntryV0Storage?: NativeEntryV0Encoder["encodeEntryV0Storage"];
|
|
270
|
+
encodeEntryV0StorageWithCid?: NativeEntryV0Encoder["encodeEntryV0StorageWithCid"];
|
|
271
|
+
prepareEntryV0PlainChain?: NativeEntryV0Encoder["prepareEntryV0PlainChain"];
|
|
272
|
+
prepareEntryV0PlainEntry?: NativeEntryV0Encoder["prepareEntryV0PlainEntry"];
|
|
273
|
+
calculateRawCidV1?: NativeEntryV0Encoder["calculateRawCidV1"];
|
|
274
|
+
calculateRawCidV1Batch?: NativeEntryV0Encoder["calculateRawCidV1Batch"];
|
|
275
|
+
entryV0PlainPayloadDataFromStorage?: NativeEntryV0Encoder[
|
|
276
|
+
"entryV0PlainPayloadDataFromStorage"
|
|
277
|
+
];
|
|
278
|
+
prepareRawEntryV0Batch?: NativeEntryV0Encoder["prepareRawEntryV0Batch"];
|
|
279
|
+
verifyEd25519Batch?: NativeEntryV0Encoder["verifyEd25519Batch"];
|
|
280
|
+
verifyEntryV0Ed25519Batch?: NativeEntryV0Encoder["verifyEntryV0Ed25519Batch"];
|
|
281
|
+
verifyEntryV0Ed25519StorageBatch?: NativeEntryV0Encoder["verifyEntryV0Ed25519StorageBatch"];
|
|
282
|
+
}): NativeEntryV0Encoder | undefined => {
|
|
283
|
+
if (
|
|
284
|
+
!mod.encodeEntryV0Signable ||
|
|
285
|
+
!mod.encodeEntryV0Storage ||
|
|
286
|
+
!mod.calculateRawCidV1
|
|
287
|
+
) {
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
encodeEntryV0Signable: mod.encodeEntryV0Signable,
|
|
292
|
+
encodeEntryV0Storage: mod.encodeEntryV0Storage,
|
|
293
|
+
encodeEntryV0StorageWithCid: mod.encodeEntryV0StorageWithCid,
|
|
294
|
+
prepareEntryV0PlainChain: mod.prepareEntryV0PlainChain,
|
|
295
|
+
prepareEntryV0PlainEntry: mod.prepareEntryV0PlainEntry,
|
|
296
|
+
calculateRawCidV1: mod.calculateRawCidV1,
|
|
297
|
+
calculateRawCidV1Batch: mod.calculateRawCidV1Batch,
|
|
298
|
+
entryV0PlainPayloadDataFromStorage:
|
|
299
|
+
mod.entryV0PlainPayloadDataFromStorage,
|
|
300
|
+
prepareRawEntryV0Batch: mod.prepareRawEntryV0Batch,
|
|
301
|
+
verifyEd25519Batch: mod.verifyEd25519Batch,
|
|
302
|
+
verifyEntryV0Ed25519Batch: mod.verifyEntryV0Ed25519Batch,
|
|
303
|
+
verifyEntryV0Ed25519StorageBatch: mod.verifyEntryV0Ed25519StorageBatch,
|
|
304
|
+
};
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const loadNativeEntryV0Encoder = ():
|
|
308
|
+
| NativeEntryV0Encoder
|
|
309
|
+
| undefined
|
|
310
|
+
| Promise<NativeEntryV0Encoder | undefined> => {
|
|
311
|
+
if (nativeEntryV0EncoderLoaded) {
|
|
312
|
+
return nativeEntryV0Encoder;
|
|
313
|
+
}
|
|
314
|
+
if (!canUseOptionalNativeModuleImports()) {
|
|
315
|
+
nativeEntryV0EncoderLoaded = true;
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
if (!nativeEntryV0EncoderPromise) {
|
|
319
|
+
try {
|
|
320
|
+
nativeEntryV0EncoderPromise = import(
|
|
321
|
+
/* @vite-ignore */ ["@peerbit", "log-rust"].join("/")
|
|
322
|
+
)
|
|
323
|
+
.then((mod) => {
|
|
324
|
+
nativeEntryV0Encoder = nativeEntryV0EncoderFromModule(mod);
|
|
325
|
+
nativeEntryV0EncoderLoaded = true;
|
|
326
|
+
return nativeEntryV0Encoder;
|
|
327
|
+
})
|
|
328
|
+
.catch(() => {
|
|
329
|
+
nativeEntryV0Encoder = undefined;
|
|
330
|
+
nativeEntryV0EncoderLoaded = true;
|
|
331
|
+
return undefined;
|
|
332
|
+
});
|
|
333
|
+
} catch {
|
|
334
|
+
nativeEntryV0Encoder = undefined;
|
|
335
|
+
nativeEntryV0EncoderLoaded = true;
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return nativeEntryV0EncoderPromise;
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export const calculateRawCidV1Batch = async (
|
|
343
|
+
blocks: Uint8Array[],
|
|
344
|
+
): Promise<string[]> => {
|
|
345
|
+
if (blocks.length === 0) {
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
const totalBytes = blocks.reduce((sum, block) => sum + block.byteLength, 0);
|
|
349
|
+
if (totalBytes < NATIVE_RAW_CID_BATCH_MIN_BYTES) {
|
|
350
|
+
// Small raw-head messages are faster through the JS hasher because they
|
|
351
|
+
// avoid copying every block into wasm.
|
|
352
|
+
return Promise.all(
|
|
353
|
+
blocks.map(async (bytes) => (await calculateRawCid(bytes)).cid),
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
const nativeEncoder = loadNativeEntryV0Encoder();
|
|
357
|
+
const resolvedNativeEncoder = isPromiseLike(nativeEncoder)
|
|
358
|
+
? await nativeEncoder
|
|
359
|
+
: nativeEncoder;
|
|
360
|
+
if (resolvedNativeEncoder?.calculateRawCidV1Batch) {
|
|
361
|
+
return resolvedNativeEncoder.calculateRawCidV1Batch(blocks);
|
|
362
|
+
}
|
|
363
|
+
return Promise.all(
|
|
364
|
+
blocks.map(async (bytes) => (await calculateRawCid(bytes)).cid),
|
|
365
|
+
);
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
export const prepareRawEntryV0Batch = async (
|
|
369
|
+
blocks: Uint8Array[],
|
|
370
|
+
): Promise<PreparedRawEntryV0Facts[] | undefined> => {
|
|
371
|
+
if (blocks.length === 0) {
|
|
372
|
+
return [];
|
|
373
|
+
}
|
|
374
|
+
const nativeEncoder = loadNativeEntryV0Encoder();
|
|
375
|
+
const resolvedNativeEncoder = isPromiseLike(nativeEncoder)
|
|
376
|
+
? await nativeEncoder
|
|
377
|
+
: nativeEncoder;
|
|
378
|
+
return resolvedNativeEncoder?.prepareRawEntryV0Batch?.(blocks);
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
export const entryV0PlainPayloadDataFromStorage = async (
|
|
382
|
+
bytes: Uint8Array,
|
|
383
|
+
): Promise<Uint8Array | undefined> => {
|
|
384
|
+
const nativeEncoder = loadNativeEntryV0Encoder();
|
|
385
|
+
const resolvedNativeEncoder = isPromiseLike(nativeEncoder)
|
|
386
|
+
? await nativeEncoder
|
|
387
|
+
: nativeEncoder;
|
|
388
|
+
return resolvedNativeEncoder?.entryV0PlainPayloadDataFromStorage?.(bytes);
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
export type Ed25519VerifyBatchInput = {
|
|
392
|
+
signature: Uint8Array;
|
|
393
|
+
publicKey: Uint8Array;
|
|
394
|
+
message: Uint8Array;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
export type EntryV0Ed25519VerifyInput = {
|
|
398
|
+
clockId: Uint8Array;
|
|
399
|
+
wallTime: bigint;
|
|
400
|
+
logical?: number;
|
|
401
|
+
gid: string;
|
|
402
|
+
next?: string[];
|
|
403
|
+
type?: number;
|
|
404
|
+
metaData?: Uint8Array;
|
|
405
|
+
payloadData: Uint8Array;
|
|
406
|
+
signature: Uint8Array;
|
|
407
|
+
publicKey: Uint8Array;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
export const verifyEd25519Batch = async (
|
|
411
|
+
inputs: Ed25519VerifyBatchInput[],
|
|
412
|
+
): Promise<boolean[] | undefined> => {
|
|
413
|
+
if (inputs.length === 0) {
|
|
414
|
+
return [];
|
|
415
|
+
}
|
|
416
|
+
const nativeEncoder = loadNativeEntryV0Encoder();
|
|
417
|
+
const resolvedNativeEncoder = isPromiseLike(nativeEncoder)
|
|
418
|
+
? await nativeEncoder
|
|
419
|
+
: nativeEncoder;
|
|
420
|
+
return resolvedNativeEncoder?.verifyEd25519Batch?.(inputs);
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
export const verifyEntryV0Ed25519Batch = async (
|
|
424
|
+
inputs: EntryV0Ed25519VerifyInput[],
|
|
425
|
+
): Promise<boolean[] | undefined> => {
|
|
426
|
+
if (inputs.length === 0) {
|
|
427
|
+
return [];
|
|
428
|
+
}
|
|
429
|
+
const nativeEncoder = loadNativeEntryV0Encoder();
|
|
430
|
+
const resolvedNativeEncoder = isPromiseLike(nativeEncoder)
|
|
431
|
+
? await nativeEncoder
|
|
432
|
+
: nativeEncoder;
|
|
433
|
+
return resolvedNativeEncoder?.verifyEntryV0Ed25519Batch?.(inputs);
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
export const verifyEntryV0Ed25519StorageBatch = async (
|
|
437
|
+
blocks: Uint8Array[],
|
|
438
|
+
): Promise<boolean[] | undefined> => {
|
|
439
|
+
if (blocks.length === 0) {
|
|
440
|
+
return [];
|
|
441
|
+
}
|
|
442
|
+
const nativeEncoder = loadNativeEntryV0Encoder();
|
|
443
|
+
const resolvedNativeEncoder = isPromiseLike(nativeEncoder)
|
|
444
|
+
? await nativeEncoder
|
|
445
|
+
: nativeEncoder;
|
|
446
|
+
return resolvedNativeEncoder?.verifyEntryV0Ed25519StorageBatch?.(blocks);
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
export const verifyEntryV0Ed25519BatchFromEntries = async (
|
|
450
|
+
entries: Entry<any>[],
|
|
451
|
+
): Promise<boolean[] | undefined> => {
|
|
452
|
+
if (entries.length === 0) {
|
|
453
|
+
return [];
|
|
454
|
+
}
|
|
455
|
+
const preparedStorageBytes: Uint8Array[] = [];
|
|
456
|
+
let allHavePreparedStorageBytes = true;
|
|
457
|
+
for (const entry of entries) {
|
|
458
|
+
const bytes = Entry.getPreparedStorageBytes(entry);
|
|
459
|
+
if (!bytes) {
|
|
460
|
+
allHavePreparedStorageBytes = false;
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
preparedStorageBytes.push(bytes);
|
|
464
|
+
}
|
|
465
|
+
if (allHavePreparedStorageBytes) {
|
|
466
|
+
try {
|
|
467
|
+
const verified =
|
|
468
|
+
await verifyEntryV0Ed25519StorageBatch(preparedStorageBytes);
|
|
469
|
+
if (verified) {
|
|
470
|
+
return verified;
|
|
471
|
+
}
|
|
472
|
+
} catch {
|
|
473
|
+
// Fall through to field extraction for encrypted or non-Ed25519 entries.
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const inputs: EntryV0Ed25519VerifyInput[] = [];
|
|
477
|
+
for (const entry of entries) {
|
|
478
|
+
if (!(entry instanceof EntryV0)) {
|
|
479
|
+
return undefined;
|
|
480
|
+
}
|
|
481
|
+
const rawEntry = entry as EntryV0<any> & {
|
|
482
|
+
_meta: unknown;
|
|
483
|
+
_payload: unknown;
|
|
484
|
+
};
|
|
485
|
+
if (
|
|
486
|
+
!(rawEntry._meta instanceof DecryptedThing) ||
|
|
487
|
+
!(rawEntry._payload instanceof DecryptedThing)
|
|
488
|
+
) {
|
|
489
|
+
return undefined;
|
|
490
|
+
}
|
|
491
|
+
let signatures: SignatureWithKey[];
|
|
492
|
+
let meta: Meta;
|
|
493
|
+
let payload: Payload<any>;
|
|
494
|
+
try {
|
|
495
|
+
signatures = entry.signatures;
|
|
496
|
+
meta = entry.meta;
|
|
497
|
+
payload = entry.payload;
|
|
498
|
+
} catch {
|
|
499
|
+
return undefined;
|
|
500
|
+
}
|
|
501
|
+
if (signatures.length !== 1) {
|
|
502
|
+
return undefined;
|
|
503
|
+
}
|
|
504
|
+
const signature = signatures[0]!;
|
|
505
|
+
if (
|
|
506
|
+
!(signature.publicKey instanceof Ed25519PublicKey) ||
|
|
507
|
+
signature.prehash !== 0
|
|
508
|
+
) {
|
|
509
|
+
return undefined;
|
|
510
|
+
}
|
|
511
|
+
inputs.push({
|
|
512
|
+
clockId: meta.clock.id,
|
|
513
|
+
wallTime: meta.clock.timestamp.wallTime,
|
|
514
|
+
logical: meta.clock.timestamp.logical,
|
|
515
|
+
gid: meta.gid,
|
|
516
|
+
next: meta.next,
|
|
517
|
+
type: meta.type,
|
|
518
|
+
metaData: meta.data,
|
|
519
|
+
payloadData: payload.data,
|
|
520
|
+
signature: signature.signature,
|
|
521
|
+
publicKey: signature.publicKey.publicKey,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
return verifyEntryV0Ed25519Batch(inputs);
|
|
525
|
+
};
|
|
39
526
|
|
|
40
527
|
export type MaybeEncryptionPublicKey =
|
|
41
528
|
| X25519PublicKey
|
|
@@ -178,6 +665,7 @@ export class EntryV0<T>
|
|
|
178
665
|
|
|
179
666
|
private _keychain?: CryptoKeychain;
|
|
180
667
|
private _encoding?: Encoding<T>;
|
|
668
|
+
private _hashDigestBytes?: Uint8Array;
|
|
181
669
|
|
|
182
670
|
constructor(obj: {
|
|
183
671
|
payload: MaybeEncrypted<Payload<T>>;
|
|
@@ -229,6 +717,18 @@ export class EntryV0<T>
|
|
|
229
717
|
return this.meta;
|
|
230
718
|
}
|
|
231
719
|
|
|
720
|
+
getMetaBytes(): Uint8Array | undefined {
|
|
721
|
+
try {
|
|
722
|
+
return this._meta.decrypted._data;
|
|
723
|
+
} catch {
|
|
724
|
+
return undefined;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
getHashDigestBytes(): Uint8Array | undefined {
|
|
729
|
+
return this._hashDigestBytes;
|
|
730
|
+
}
|
|
731
|
+
|
|
232
732
|
async getClock(): Promise<Clock> {
|
|
233
733
|
return (await this.getMeta()).clock;
|
|
234
734
|
}
|
|
@@ -395,7 +895,916 @@ export class EntryV0<T>
|
|
|
395
895
|
}
|
|
396
896
|
|
|
397
897
|
static createGid(seed?: Uint8Array): Promise<string> | string {
|
|
398
|
-
return seed ? sha256Base64(seed) :
|
|
898
|
+
return seed ? sha256Base64(seed) : createRandomGid();
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
static createGids(count: number): string[] {
|
|
902
|
+
if (count === 0) {
|
|
903
|
+
return [];
|
|
904
|
+
}
|
|
905
|
+
const bytes = randomBytes(count * RANDOM_GID_BYTES);
|
|
906
|
+
const gids = new Array<string>(count);
|
|
907
|
+
for (let i = 0; i < count; i++) {
|
|
908
|
+
const offset = i * RANDOM_GID_BYTES;
|
|
909
|
+
gids[i] = toBase64(
|
|
910
|
+
bytes.subarray(offset, offset + RANDOM_GID_BYTES),
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
return gids;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
static async createPlainAppendChain<T>(properties: {
|
|
917
|
+
data: T[];
|
|
918
|
+
payloadDatas?: Uint8Array[];
|
|
919
|
+
meta?: {
|
|
920
|
+
clocks: () => Clock[];
|
|
921
|
+
gid?: string;
|
|
922
|
+
type?: EntryType;
|
|
923
|
+
gidSeed?: Uint8Array;
|
|
924
|
+
data?: Uint8Array;
|
|
925
|
+
next?: SortableEntry[];
|
|
926
|
+
};
|
|
927
|
+
encoding: Encoding<T>;
|
|
928
|
+
identity: Identity;
|
|
929
|
+
deferStore: boolean;
|
|
930
|
+
cachePreparedEntries?: boolean;
|
|
931
|
+
nativeGraph?: NativeEntryV0Graph;
|
|
932
|
+
nativeBlockStore?: unknown;
|
|
933
|
+
}): Promise<Entry<T>[] | undefined> {
|
|
934
|
+
return (await EntryV0.createPlainAppendChainBatch(properties))?.entries;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
static async createPlainAppendEntriesBatch<T>(properties: {
|
|
938
|
+
data: T[];
|
|
939
|
+
payloadDatas?: Uint8Array[];
|
|
940
|
+
meta: {
|
|
941
|
+
clocks: () => Clock[];
|
|
942
|
+
gids: string[];
|
|
943
|
+
nexts?: SortableEntry[][];
|
|
944
|
+
type?: EntryType;
|
|
945
|
+
datas?: Array<Uint8Array | undefined>;
|
|
946
|
+
};
|
|
947
|
+
encoding: Encoding<T>;
|
|
948
|
+
identity: Identity;
|
|
949
|
+
deferStore: boolean;
|
|
950
|
+
cachePreparedEntries?: boolean;
|
|
951
|
+
nativeGraph?: NativeEntryV0Graph;
|
|
952
|
+
nativeBlockStore?: unknown;
|
|
953
|
+
}): Promise<PreparedAppendChain<T> | undefined> {
|
|
954
|
+
if (!properties.deferStore) {
|
|
955
|
+
return undefined;
|
|
956
|
+
}
|
|
957
|
+
if (!(properties.identity instanceof Ed25519Keypair)) {
|
|
958
|
+
return undefined;
|
|
959
|
+
}
|
|
960
|
+
const nativeCommit =
|
|
961
|
+
properties.nativeGraph?.prepareEntryV0PlainEntriesCommit;
|
|
962
|
+
if (!nativeCommit) {
|
|
963
|
+
return undefined;
|
|
964
|
+
}
|
|
965
|
+
if (
|
|
966
|
+
properties.meta.gids.length !== properties.data.length ||
|
|
967
|
+
(properties.meta.nexts &&
|
|
968
|
+
properties.meta.nexts.length !== properties.data.length) ||
|
|
969
|
+
(properties.payloadDatas &&
|
|
970
|
+
properties.payloadDatas.length !== properties.data.length)
|
|
971
|
+
) {
|
|
972
|
+
throw new Error("Expected one gid and next list per entry");
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
const clocks = properties.meta.clocks();
|
|
976
|
+
if (clocks.length !== properties.data.length) {
|
|
977
|
+
throw new Error("Expected one clock per entry");
|
|
978
|
+
}
|
|
979
|
+
for (let i = 0; i < clocks.length; i++) {
|
|
980
|
+
for (const next of properties.meta.nexts?.[i] ?? []) {
|
|
981
|
+
if (
|
|
982
|
+
Timestamp.compare(next.meta.clock.timestamp, clocks[i]!.timestamp) >=
|
|
983
|
+
0
|
|
984
|
+
) {
|
|
985
|
+
throw new Error(
|
|
986
|
+
"Expecting next(s) to happen before entry, got: " +
|
|
987
|
+
next.meta.clock.timestamp +
|
|
988
|
+
" > " +
|
|
989
|
+
clocks[i]!.timestamp,
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
const payloadDatas =
|
|
996
|
+
properties.payloadDatas ??
|
|
997
|
+
properties.data.map((data) => properties.encoding.encoder(data));
|
|
998
|
+
const input: NativePlainEntriesInput = {
|
|
999
|
+
clockId: properties.identity.publicKey.bytes,
|
|
1000
|
+
privateKey: properties.identity.privateKey.privateKey,
|
|
1001
|
+
publicKey: properties.identity.publicKey.publicKey,
|
|
1002
|
+
wallTimes: clocks.map((clock) => clock.timestamp.wallTime),
|
|
1003
|
+
logicals: clocks.map((clock) => clock.timestamp.logical),
|
|
1004
|
+
gids: properties.meta.gids,
|
|
1005
|
+
nexts: properties.meta.nexts?.map((nexts) =>
|
|
1006
|
+
nexts.map((next) => next.hash),
|
|
1007
|
+
),
|
|
1008
|
+
type: properties.meta.type,
|
|
1009
|
+
metaDatas: properties.meta.datas,
|
|
1010
|
+
payloadDatas,
|
|
1011
|
+
};
|
|
1012
|
+
const preparedValue = nativeCommit.call(
|
|
1013
|
+
properties.nativeGraph,
|
|
1014
|
+
input,
|
|
1015
|
+
properties.nativeBlockStore,
|
|
1016
|
+
);
|
|
1017
|
+
const prepared = isPromiseLike(preparedValue)
|
|
1018
|
+
? await preparedValue
|
|
1019
|
+
: preparedValue;
|
|
1020
|
+
if (!prepared) {
|
|
1021
|
+
return undefined;
|
|
1022
|
+
}
|
|
1023
|
+
if (prepared.length !== properties.data.length) {
|
|
1024
|
+
throw new Error("Unexpected prepared entry batch length");
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
const entries: PreparedAppendChain<T>["entries"] = [];
|
|
1028
|
+
const shallowEntries: PreparedAppendChain<T>["shallowEntries"] = [];
|
|
1029
|
+
const appendFacts: NonNullable<PreparedAppendChain<T>["appendFacts"]> = [];
|
|
1030
|
+
const nativeEntries: NonNullable<PreparedAppendChain<T>["nativeEntries"]> =
|
|
1031
|
+
[];
|
|
1032
|
+
const entryType = properties.meta.type ?? EntryType.APPEND;
|
|
1033
|
+
for (let index = 0; index < prepared.length; index++) {
|
|
1034
|
+
const preparedEntry = prepared[index]!;
|
|
1035
|
+
const clock = clocks[index]!;
|
|
1036
|
+
const entryGid = properties.meta.gids[index]!;
|
|
1037
|
+
const metaData = properties.meta.datas?.[index];
|
|
1038
|
+
const meta = new Meta({
|
|
1039
|
+
clock,
|
|
1040
|
+
gid: entryGid,
|
|
1041
|
+
type: entryType,
|
|
1042
|
+
data: metaData,
|
|
1043
|
+
next: preparedEntry.next,
|
|
1044
|
+
});
|
|
1045
|
+
const payloadSize = payloadDatas[index]!.byteLength;
|
|
1046
|
+
const payload =
|
|
1047
|
+
properties.cachePreparedEntries === false
|
|
1048
|
+
? undefined
|
|
1049
|
+
: new Payload<T>({
|
|
1050
|
+
data: payloadDatas[index]!,
|
|
1051
|
+
value: properties.data[index],
|
|
1052
|
+
encoding: properties.encoding,
|
|
1053
|
+
});
|
|
1054
|
+
const signature =
|
|
1055
|
+
properties.cachePreparedEntries === false
|
|
1056
|
+
? undefined
|
|
1057
|
+
: new SignatureWithKey({
|
|
1058
|
+
signature: preparedEntry.signature!,
|
|
1059
|
+
publicKey: properties.identity.publicKey,
|
|
1060
|
+
prehash: 0,
|
|
1061
|
+
});
|
|
1062
|
+
const entry = new EntryV0<T>({
|
|
1063
|
+
meta: new DecryptedThing({
|
|
1064
|
+
data: preparedEntry.metaBytes,
|
|
1065
|
+
value: meta,
|
|
1066
|
+
}),
|
|
1067
|
+
payload: new DecryptedThing({
|
|
1068
|
+
data: preparedEntry.payloadBytes,
|
|
1069
|
+
value: payload,
|
|
1070
|
+
}),
|
|
1071
|
+
signatures: new Signatures({
|
|
1072
|
+
signatures: [
|
|
1073
|
+
new DecryptedThing({
|
|
1074
|
+
data: preparedEntry.signatureBytes,
|
|
1075
|
+
value: signature,
|
|
1076
|
+
}),
|
|
1077
|
+
],
|
|
1078
|
+
}),
|
|
1079
|
+
createdLocally: true,
|
|
1080
|
+
});
|
|
1081
|
+
if (properties.cachePreparedEntries === false) {
|
|
1082
|
+
entry.hash = preparedEntry.cid;
|
|
1083
|
+
entry.size = preparedEntry.byteLength;
|
|
1084
|
+
} else {
|
|
1085
|
+
if (!preparedEntry.bytes) {
|
|
1086
|
+
throw new Error("Missing prepared entry bytes");
|
|
1087
|
+
}
|
|
1088
|
+
entry.hash = Entry.prepareMultihashBytes(
|
|
1089
|
+
entry,
|
|
1090
|
+
preparedEntry.bytes,
|
|
1091
|
+
preparedEntry.cid,
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
entry._hashDigestBytes = preparedEntry.hashDigestBytes;
|
|
1095
|
+
const shallowEntry = new ShallowEntry({
|
|
1096
|
+
hash: entry.hash,
|
|
1097
|
+
payloadSize,
|
|
1098
|
+
head: true,
|
|
1099
|
+
meta: new ShallowMeta({
|
|
1100
|
+
gid: entryGid,
|
|
1101
|
+
data: metaData,
|
|
1102
|
+
clock,
|
|
1103
|
+
next: preparedEntry.next,
|
|
1104
|
+
type: entryType,
|
|
1105
|
+
}),
|
|
1106
|
+
});
|
|
1107
|
+
const nativeEntry =
|
|
1108
|
+
properties.cachePreparedEntries === false
|
|
1109
|
+
? undefined
|
|
1110
|
+
: {
|
|
1111
|
+
hash: entry.hash,
|
|
1112
|
+
gid: entryGid,
|
|
1113
|
+
next: preparedEntry.next,
|
|
1114
|
+
type: entryType,
|
|
1115
|
+
head: true,
|
|
1116
|
+
payloadSize,
|
|
1117
|
+
data: metaData,
|
|
1118
|
+
clock: {
|
|
1119
|
+
timestamp: {
|
|
1120
|
+
wallTime: clock.timestamp.wallTime,
|
|
1121
|
+
logical: clock.timestamp.logical,
|
|
1122
|
+
},
|
|
1123
|
+
},
|
|
1124
|
+
};
|
|
1125
|
+
if (properties.cachePreparedEntries !== false) {
|
|
1126
|
+
Entry.prepareShallowEntry(entry, shallowEntry);
|
|
1127
|
+
Entry.prepareNativeLogEntry(entry, nativeEntry!);
|
|
1128
|
+
entry.init({ encoding: properties.encoding });
|
|
1129
|
+
}
|
|
1130
|
+
entries.push(entry);
|
|
1131
|
+
shallowEntries.push(shallowEntry);
|
|
1132
|
+
appendFacts.push({
|
|
1133
|
+
hash: entry.hash,
|
|
1134
|
+
gid: entryGid,
|
|
1135
|
+
next: preparedEntry.next,
|
|
1136
|
+
wallTime: clock.timestamp.wallTime,
|
|
1137
|
+
logical: clock.timestamp.logical,
|
|
1138
|
+
clockId: clock.id,
|
|
1139
|
+
type: entryType,
|
|
1140
|
+
metaData,
|
|
1141
|
+
payloadSize,
|
|
1142
|
+
metaBytes: preparedEntry.metaBytes,
|
|
1143
|
+
hashDigestBytes: preparedEntry.hashDigestBytes,
|
|
1144
|
+
});
|
|
1145
|
+
if (nativeEntry) {
|
|
1146
|
+
nativeEntries.push(nativeEntry);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
return {
|
|
1151
|
+
entries,
|
|
1152
|
+
shallowEntries,
|
|
1153
|
+
appendFacts,
|
|
1154
|
+
nativeEntries,
|
|
1155
|
+
nativeGraphUpdated: true,
|
|
1156
|
+
nativeBlocksCommitted: true,
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
static createPlainAppendChainCommitOnly<T>(
|
|
1161
|
+
properties: PlainAppendChainCommitOnlyProperties<T>,
|
|
1162
|
+
): MaybePromise<PreparedAppendCommitOnlyChain<T> | undefined> {
|
|
1163
|
+
if (!properties.deferStore || properties.data.length !== 1) {
|
|
1164
|
+
return undefined;
|
|
1165
|
+
}
|
|
1166
|
+
if (!(properties.identity instanceof Ed25519Keypair)) {
|
|
1167
|
+
return undefined;
|
|
1168
|
+
}
|
|
1169
|
+
if (
|
|
1170
|
+
!properties.nativeGraph?.prepareEntryV0PlainEntryCommit &&
|
|
1171
|
+
!properties.nativeGraph?.prepareEntryV0PlainEntryAndPut
|
|
1172
|
+
) {
|
|
1173
|
+
return undefined;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
const nexts = properties.meta?.next ?? [];
|
|
1177
|
+
const nextHashes: string[] = [];
|
|
1178
|
+
let gid: string | null = null;
|
|
1179
|
+
if (nexts.length > 0) {
|
|
1180
|
+
if (properties.meta?.gid) {
|
|
1181
|
+
throw new Error(
|
|
1182
|
+
"Expecting '.meta.gid' property to be undefined if '.meta.next' is provided",
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
for (const next of nexts) {
|
|
1186
|
+
if (!next.hash) {
|
|
1187
|
+
throw new Error("Expecting hash to be defined to next entries");
|
|
1188
|
+
}
|
|
1189
|
+
nextHashes.push(next.hash);
|
|
1190
|
+
gid =
|
|
1191
|
+
gid == null
|
|
1192
|
+
? next.meta.gid
|
|
1193
|
+
: next.meta.gid < (gid as string)
|
|
1194
|
+
? next.meta.gid
|
|
1195
|
+
: gid;
|
|
1196
|
+
}
|
|
1197
|
+
} else if (properties.meta?.gid) {
|
|
1198
|
+
gid = properties.meta.gid;
|
|
1199
|
+
} else {
|
|
1200
|
+
const createdGid = EntryV0.createGid(properties.meta?.gidSeed);
|
|
1201
|
+
return isPromiseLike(createdGid)
|
|
1202
|
+
? createdGid.then((resolvedGid) =>
|
|
1203
|
+
EntryV0.finishPlainAppendChainCommitOnly(
|
|
1204
|
+
properties,
|
|
1205
|
+
nexts,
|
|
1206
|
+
nextHashes,
|
|
1207
|
+
resolvedGid,
|
|
1208
|
+
),
|
|
1209
|
+
)
|
|
1210
|
+
: EntryV0.finishPlainAppendChainCommitOnly(
|
|
1211
|
+
properties,
|
|
1212
|
+
nexts,
|
|
1213
|
+
nextHashes,
|
|
1214
|
+
createdGid,
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
return EntryV0.finishPlainAppendChainCommitOnly(
|
|
1219
|
+
properties,
|
|
1220
|
+
nexts,
|
|
1221
|
+
nextHashes,
|
|
1222
|
+
gid!,
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
private static finishPlainAppendChainCommitOnly<T>(
|
|
1227
|
+
properties: PlainAppendChainCommitOnlyProperties<T>,
|
|
1228
|
+
nexts: SortableEntry[],
|
|
1229
|
+
nextHashes: string[],
|
|
1230
|
+
gid: string,
|
|
1231
|
+
): MaybePromise<PreparedAppendCommitOnlyChain<T> | undefined> {
|
|
1232
|
+
if (
|
|
1233
|
+
!properties.nativeGraph ||
|
|
1234
|
+
!(properties.identity instanceof Ed25519Keypair)
|
|
1235
|
+
) {
|
|
1236
|
+
return undefined;
|
|
1237
|
+
}
|
|
1238
|
+
const nativeGraph = properties.nativeGraph;
|
|
1239
|
+
const identity = properties.identity;
|
|
1240
|
+
const clocks = properties.meta?.clocks();
|
|
1241
|
+
if (!clocks || clocks.length !== 1) {
|
|
1242
|
+
throw new Error("Expected one clock per entry");
|
|
1243
|
+
}
|
|
1244
|
+
const clock = clocks[0]!;
|
|
1245
|
+
for (const next of nexts) {
|
|
1246
|
+
if (Timestamp.compare(next.meta.clock.timestamp, clock.timestamp) >= 0) {
|
|
1247
|
+
throw new Error(
|
|
1248
|
+
"Expecting next(s) to happen before entry, got: " +
|
|
1249
|
+
next.meta.clock.timestamp +
|
|
1250
|
+
" > " +
|
|
1251
|
+
clock.timestamp,
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
const payloadData =
|
|
1257
|
+
properties.payloadDatas?.[0] ??
|
|
1258
|
+
properties.encoding.encoder(properties.data[0]!);
|
|
1259
|
+
const entryType = properties.meta?.type ?? EntryType.APPEND;
|
|
1260
|
+
const singleInput: NativePlainEntryInput = {
|
|
1261
|
+
clockId: identity.publicKey.bytes,
|
|
1262
|
+
privateKey: identity.privateKey.privateKey,
|
|
1263
|
+
publicKey: identity.publicKey.publicKey,
|
|
1264
|
+
wallTime: clock.timestamp.wallTime,
|
|
1265
|
+
logical: clock.timestamp.logical,
|
|
1266
|
+
gid: gid!,
|
|
1267
|
+
next: nextHashes,
|
|
1268
|
+
type: entryType,
|
|
1269
|
+
metaData: properties.meta?.data,
|
|
1270
|
+
payloadData,
|
|
1271
|
+
includeMaterializationBytes: properties.includeMaterializationBytes,
|
|
1272
|
+
includeAppendFactsBytes: properties.includeAppendFactsBytes,
|
|
1273
|
+
trimLengthTo: properties.nativeTrimLengthTo,
|
|
1274
|
+
resolveTrimmedEntries:
|
|
1275
|
+
properties.nativeTrimLengthTo == null ? undefined : false,
|
|
1276
|
+
};
|
|
1277
|
+
let nativeBlocksCommitted = false;
|
|
1278
|
+
let nativeGraphUpdated = false;
|
|
1279
|
+
const finishWithPreparedEntry = (
|
|
1280
|
+
preparedEntry: NativePreparedPlainEntry | undefined,
|
|
1281
|
+
): PreparedAppendCommitOnlyChain<T> | undefined => {
|
|
1282
|
+
if (!preparedEntry) {
|
|
1283
|
+
return undefined;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
const payloadSize = payloadData.byteLength;
|
|
1287
|
+
const shallowEntry = new ShallowEntry({
|
|
1288
|
+
hash: preparedEntry.cid,
|
|
1289
|
+
payloadSize,
|
|
1290
|
+
head: true,
|
|
1291
|
+
meta: new ShallowMeta({
|
|
1292
|
+
gid: gid!,
|
|
1293
|
+
data: properties.meta?.data,
|
|
1294
|
+
clock,
|
|
1295
|
+
next: preparedEntry.next,
|
|
1296
|
+
type: entryType,
|
|
1297
|
+
}),
|
|
1298
|
+
});
|
|
1299
|
+
const appendFacts = {
|
|
1300
|
+
hash: preparedEntry.cid,
|
|
1301
|
+
gid: gid!,
|
|
1302
|
+
next: preparedEntry.next,
|
|
1303
|
+
wallTime: clock.timestamp.wallTime,
|
|
1304
|
+
logical: clock.timestamp.logical,
|
|
1305
|
+
clockId: clock.id,
|
|
1306
|
+
type: entryType,
|
|
1307
|
+
metaData: properties.meta?.data,
|
|
1308
|
+
payloadSize,
|
|
1309
|
+
metaBytes: preparedEntry.metaBytes,
|
|
1310
|
+
hashDigestBytes: preparedEntry.hashDigestBytes,
|
|
1311
|
+
};
|
|
1312
|
+
let materialized: Entry<T> | undefined;
|
|
1313
|
+
const materializeEntry = (index = 0): Entry<T> => {
|
|
1314
|
+
if (index !== 0) {
|
|
1315
|
+
throw new Error("Prepared commit-only append only has one entry");
|
|
1316
|
+
}
|
|
1317
|
+
if (materialized) {
|
|
1318
|
+
return materialized;
|
|
1319
|
+
}
|
|
1320
|
+
const payload = new Payload<T>({
|
|
1321
|
+
data: payloadData,
|
|
1322
|
+
value: properties.data[0],
|
|
1323
|
+
encoding: properties.encoding,
|
|
1324
|
+
});
|
|
1325
|
+
if (
|
|
1326
|
+
!preparedEntry.metaBytes ||
|
|
1327
|
+
!preparedEntry.payloadBytes ||
|
|
1328
|
+
!preparedEntry.signature ||
|
|
1329
|
+
!preparedEntry.signatureBytes
|
|
1330
|
+
) {
|
|
1331
|
+
const bytes =
|
|
1332
|
+
preparedEntry.bytes ??
|
|
1333
|
+
getSynchronousBlockBytes(
|
|
1334
|
+
properties.nativeBlockStore,
|
|
1335
|
+
preparedEntry.cid,
|
|
1336
|
+
);
|
|
1337
|
+
if (!bytes) {
|
|
1338
|
+
throw new Error("Missing prepared entry bytes");
|
|
1339
|
+
}
|
|
1340
|
+
const entry = deserialize(bytes, Entry) as EntryV0<T>;
|
|
1341
|
+
entry.hash = preparedEntry.cid;
|
|
1342
|
+
entry.size = preparedEntry.byteLength;
|
|
1343
|
+
entry.createdLocally = true;
|
|
1344
|
+
entry._hashDigestBytes = preparedEntry.hashDigestBytes;
|
|
1345
|
+
Entry.prepareShallowEntry(entry, shallowEntry);
|
|
1346
|
+
entry.init({ encoding: properties.encoding });
|
|
1347
|
+
materialized = entry;
|
|
1348
|
+
return entry;
|
|
1349
|
+
}
|
|
1350
|
+
const signature = new SignatureWithKey({
|
|
1351
|
+
signature: preparedEntry.signature,
|
|
1352
|
+
publicKey: properties.identity.publicKey,
|
|
1353
|
+
prehash: 0,
|
|
1354
|
+
});
|
|
1355
|
+
const meta = new Meta({
|
|
1356
|
+
clock,
|
|
1357
|
+
gid: gid!,
|
|
1358
|
+
type: entryType,
|
|
1359
|
+
data: properties.meta?.data,
|
|
1360
|
+
next: preparedEntry.next,
|
|
1361
|
+
});
|
|
1362
|
+
const entry = new EntryV0<T>({
|
|
1363
|
+
meta: new DecryptedThing({
|
|
1364
|
+
data: preparedEntry.metaBytes,
|
|
1365
|
+
value: meta,
|
|
1366
|
+
}),
|
|
1367
|
+
payload: new DecryptedThing({
|
|
1368
|
+
data: preparedEntry.payloadBytes,
|
|
1369
|
+
value: payload,
|
|
1370
|
+
}),
|
|
1371
|
+
signatures: new Signatures({
|
|
1372
|
+
signatures: [
|
|
1373
|
+
new DecryptedThing({
|
|
1374
|
+
data: preparedEntry.signatureBytes,
|
|
1375
|
+
value: signature,
|
|
1376
|
+
}),
|
|
1377
|
+
],
|
|
1378
|
+
}),
|
|
1379
|
+
createdLocally: true,
|
|
1380
|
+
});
|
|
1381
|
+
entry.hash = preparedEntry.cid;
|
|
1382
|
+
entry.size = preparedEntry.byteLength;
|
|
1383
|
+
entry._hashDigestBytes = preparedEntry.hashDigestBytes;
|
|
1384
|
+
Entry.prepareShallowEntry(entry, shallowEntry);
|
|
1385
|
+
entry.init({ encoding: properties.encoding });
|
|
1386
|
+
materialized = entry;
|
|
1387
|
+
return entry;
|
|
1388
|
+
};
|
|
1389
|
+
const preparedBlock =
|
|
1390
|
+
preparedEntry.bytes && !nativeBlocksCommitted
|
|
1391
|
+
? Entry.preparedBlockFromBytes(preparedEntry.bytes, preparedEntry.cid)
|
|
1392
|
+
: undefined;
|
|
1393
|
+
|
|
1394
|
+
return {
|
|
1395
|
+
materializeEntry,
|
|
1396
|
+
materializeEntries: () => [materializeEntry(0)],
|
|
1397
|
+
blocks: preparedBlock ? [preparedBlock] : undefined,
|
|
1398
|
+
shallowEntries: [shallowEntry],
|
|
1399
|
+
appendFacts: [appendFacts],
|
|
1400
|
+
trimmedNativeEntries: preparedEntry.trimmedEntries,
|
|
1401
|
+
trimmedNativeEntryHashes: preparedEntry.trimmedEntryHashes,
|
|
1402
|
+
trimmedNativeBlocksDeleted: nativeBlocksCommitted,
|
|
1403
|
+
nativeGraphUpdated,
|
|
1404
|
+
nativeBlocksCommitted,
|
|
1405
|
+
};
|
|
1406
|
+
};
|
|
1407
|
+
const prepareAndPutFallback = ():
|
|
1408
|
+
| PreparedAppendCommitOnlyChain<T>
|
|
1409
|
+
| undefined
|
|
1410
|
+
| Promise<PreparedAppendCommitOnlyChain<T> | undefined> => {
|
|
1411
|
+
const nativePrepareAndPut = nativeGraph.prepareEntryV0PlainEntryAndPut;
|
|
1412
|
+
const preparedEntryValue = nativePrepareAndPut
|
|
1413
|
+
? nativePrepareAndPut.call(nativeGraph, {
|
|
1414
|
+
...singleInput,
|
|
1415
|
+
})
|
|
1416
|
+
: undefined;
|
|
1417
|
+
if (isPromiseLike(preparedEntryValue)) {
|
|
1418
|
+
return preparedEntryValue.then((preparedEntry) => {
|
|
1419
|
+
nativeGraphUpdated = !!preparedEntry && !!nativePrepareAndPut;
|
|
1420
|
+
return finishWithPreparedEntry(preparedEntry);
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
nativeGraphUpdated = !!preparedEntryValue && !!nativePrepareAndPut;
|
|
1424
|
+
return finishWithPreparedEntry(preparedEntryValue);
|
|
1425
|
+
};
|
|
1426
|
+
const nativeCommit = nativeGraph.prepareEntryV0PlainEntryCommit;
|
|
1427
|
+
if (nativeCommit) {
|
|
1428
|
+
const preparedEntryValue = nativeCommit.call(
|
|
1429
|
+
nativeGraph,
|
|
1430
|
+
singleInput,
|
|
1431
|
+
properties.nativeBlockStore,
|
|
1432
|
+
);
|
|
1433
|
+
if (isPromiseLike(preparedEntryValue)) {
|
|
1434
|
+
return preparedEntryValue.then((preparedEntry) => {
|
|
1435
|
+
if (preparedEntry) {
|
|
1436
|
+
nativeBlocksCommitted = true;
|
|
1437
|
+
nativeGraphUpdated = true;
|
|
1438
|
+
return finishWithPreparedEntry(preparedEntry);
|
|
1439
|
+
}
|
|
1440
|
+
return prepareAndPutFallback();
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
if (preparedEntryValue) {
|
|
1444
|
+
nativeBlocksCommitted = true;
|
|
1445
|
+
nativeGraphUpdated = true;
|
|
1446
|
+
return finishWithPreparedEntry(preparedEntryValue);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
return prepareAndPutFallback();
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
static async createPlainAppendChainBatch<T>(properties: {
|
|
1453
|
+
data: T[];
|
|
1454
|
+
payloadDatas?: Uint8Array[];
|
|
1455
|
+
meta?: {
|
|
1456
|
+
clocks: () => Clock[];
|
|
1457
|
+
gid?: string;
|
|
1458
|
+
type?: EntryType;
|
|
1459
|
+
gidSeed?: Uint8Array;
|
|
1460
|
+
data?: Uint8Array;
|
|
1461
|
+
next?: SortableEntry[];
|
|
1462
|
+
};
|
|
1463
|
+
encoding: Encoding<T>;
|
|
1464
|
+
identity: Identity;
|
|
1465
|
+
deferStore: boolean;
|
|
1466
|
+
cachePreparedEntries?: boolean;
|
|
1467
|
+
nativeGraph?: NativeEntryV0Graph;
|
|
1468
|
+
nativeBlockStore?: unknown;
|
|
1469
|
+
}): Promise<PreparedAppendChain<T> | undefined> {
|
|
1470
|
+
if (!properties.deferStore) {
|
|
1471
|
+
return undefined;
|
|
1472
|
+
}
|
|
1473
|
+
if (!(properties.identity instanceof Ed25519Keypair)) {
|
|
1474
|
+
return undefined;
|
|
1475
|
+
}
|
|
1476
|
+
const nativeEncoderValue = loadNativeEntryV0Encoder();
|
|
1477
|
+
const nativeEncoder = isPromiseLike(nativeEncoderValue)
|
|
1478
|
+
? await nativeEncoderValue
|
|
1479
|
+
: nativeEncoderValue;
|
|
1480
|
+
if (!nativeEncoder?.prepareEntryV0PlainChain) {
|
|
1481
|
+
return undefined;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
const nexts = properties.meta?.next ?? [];
|
|
1485
|
+
const nextHashes: string[] = [];
|
|
1486
|
+
let gid: string | null = null;
|
|
1487
|
+
if (nexts.length > 0) {
|
|
1488
|
+
if (properties.meta?.gid) {
|
|
1489
|
+
throw new Error(
|
|
1490
|
+
"Expecting '.meta.gid' property to be undefined if '.meta.next' is provided",
|
|
1491
|
+
);
|
|
1492
|
+
}
|
|
1493
|
+
for (const next of nexts) {
|
|
1494
|
+
if (!next.hash) {
|
|
1495
|
+
throw new Error("Expecting hash to be defined to next entries");
|
|
1496
|
+
}
|
|
1497
|
+
nextHashes.push(next.hash);
|
|
1498
|
+
gid =
|
|
1499
|
+
gid == null
|
|
1500
|
+
? next.meta.gid
|
|
1501
|
+
: next.meta.gid < (gid as string)
|
|
1502
|
+
? next.meta.gid
|
|
1503
|
+
: gid;
|
|
1504
|
+
}
|
|
1505
|
+
} else {
|
|
1506
|
+
if (properties.meta?.gid) {
|
|
1507
|
+
gid = properties.meta.gid;
|
|
1508
|
+
} else {
|
|
1509
|
+
const createdGid = EntryV0.createGid(properties.meta?.gidSeed);
|
|
1510
|
+
gid = isPromiseLike(createdGid) ? await createdGid : createdGid;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
const clocks = properties.meta?.clocks();
|
|
1515
|
+
if (!clocks || clocks.length !== properties.data.length) {
|
|
1516
|
+
throw new Error("Expected one clock per entry");
|
|
1517
|
+
}
|
|
1518
|
+
for (const next of nexts) {
|
|
1519
|
+
if (
|
|
1520
|
+
Timestamp.compare(next.meta.clock.timestamp, clocks[0]!.timestamp) >= 0
|
|
1521
|
+
) {
|
|
1522
|
+
throw new Error(
|
|
1523
|
+
"Expecting next(s) to happen before entry, got: " +
|
|
1524
|
+
next.meta.clock.timestamp +
|
|
1525
|
+
" > " +
|
|
1526
|
+
clocks[0]!.timestamp,
|
|
1527
|
+
);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
for (let i = 1; i < clocks.length; i++) {
|
|
1531
|
+
if (
|
|
1532
|
+
Timestamp.compare(clocks[i - 1]!.timestamp, clocks[i]!.timestamp) >= 0
|
|
1533
|
+
) {
|
|
1534
|
+
throw new Error(
|
|
1535
|
+
"Expecting generated clocks to increase across appendMany",
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
const entryType = properties.meta?.type ?? EntryType.APPEND;
|
|
1541
|
+
const clockId = properties.identity.publicKey.bytes;
|
|
1542
|
+
const privateKey = properties.identity.privateKey.privateKey;
|
|
1543
|
+
const publicKey = properties.identity.publicKey.publicKey;
|
|
1544
|
+
let nativeBlocksCommitted = false;
|
|
1545
|
+
let nativeGraphUpdated = false;
|
|
1546
|
+
let prepared: NativePreparedPlainEntry[] | undefined;
|
|
1547
|
+
let payloadDatas: Uint8Array[] | undefined;
|
|
1548
|
+
if (properties.data.length === 1) {
|
|
1549
|
+
const clock = clocks[0]!;
|
|
1550
|
+
const payloadData =
|
|
1551
|
+
properties.payloadDatas?.[0] ??
|
|
1552
|
+
properties.encoding.encoder(properties.data[0]!);
|
|
1553
|
+
const singleInput: NativePlainEntryInput = {
|
|
1554
|
+
clockId,
|
|
1555
|
+
privateKey,
|
|
1556
|
+
publicKey,
|
|
1557
|
+
wallTime: clock.timestamp.wallTime,
|
|
1558
|
+
logical: clock.timestamp.logical,
|
|
1559
|
+
gid: gid!,
|
|
1560
|
+
next: nextHashes,
|
|
1561
|
+
type: entryType,
|
|
1562
|
+
metaData: properties.meta?.data,
|
|
1563
|
+
payloadData,
|
|
1564
|
+
};
|
|
1565
|
+
const nativeCommit =
|
|
1566
|
+
properties.nativeGraph?.prepareEntryV0PlainEntryCommit;
|
|
1567
|
+
const preparedEntryValue = nativeCommit
|
|
1568
|
+
? nativeCommit.call(
|
|
1569
|
+
properties.nativeGraph,
|
|
1570
|
+
singleInput,
|
|
1571
|
+
properties.nativeBlockStore,
|
|
1572
|
+
)
|
|
1573
|
+
: undefined;
|
|
1574
|
+
const preparedEntry = isPromiseLike(preparedEntryValue)
|
|
1575
|
+
? await preparedEntryValue
|
|
1576
|
+
: preparedEntryValue;
|
|
1577
|
+
if (preparedEntry) {
|
|
1578
|
+
nativeBlocksCommitted = true;
|
|
1579
|
+
nativeGraphUpdated = true;
|
|
1580
|
+
prepared = [preparedEntry];
|
|
1581
|
+
payloadDatas = [payloadData];
|
|
1582
|
+
} else {
|
|
1583
|
+
const nativePrepareAndPut =
|
|
1584
|
+
properties.nativeGraph?.prepareEntryV0PlainEntryAndPut;
|
|
1585
|
+
const scalarPreparedEntryValue = nativePrepareAndPut
|
|
1586
|
+
? nativePrepareAndPut.call(properties.nativeGraph, singleInput)
|
|
1587
|
+
: nativeEncoder.prepareEntryV0PlainEntry?.(singleInput);
|
|
1588
|
+
const scalarPreparedEntry = isPromiseLike(scalarPreparedEntryValue)
|
|
1589
|
+
? await scalarPreparedEntryValue
|
|
1590
|
+
: scalarPreparedEntryValue;
|
|
1591
|
+
if (scalarPreparedEntry) {
|
|
1592
|
+
nativeGraphUpdated = !!nativePrepareAndPut;
|
|
1593
|
+
prepared = [scalarPreparedEntry];
|
|
1594
|
+
payloadDatas = [payloadData];
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
if (!prepared) {
|
|
1600
|
+
const wallTimes = new Array<bigint | number | string>(
|
|
1601
|
+
properties.data.length,
|
|
1602
|
+
);
|
|
1603
|
+
const logicals = new Array<number>(properties.data.length);
|
|
1604
|
+
const metaDatas = new Array<Uint8Array | undefined>(
|
|
1605
|
+
properties.data.length,
|
|
1606
|
+
);
|
|
1607
|
+
if (
|
|
1608
|
+
properties.payloadDatas &&
|
|
1609
|
+
properties.payloadDatas.length !== properties.data.length
|
|
1610
|
+
) {
|
|
1611
|
+
throw new Error("Expected one payload data value per entry");
|
|
1612
|
+
}
|
|
1613
|
+
payloadDatas = properties.payloadDatas
|
|
1614
|
+
? [...properties.payloadDatas]
|
|
1615
|
+
: new Array<Uint8Array>(properties.data.length);
|
|
1616
|
+
for (let i = 0; i < properties.data.length; i++) {
|
|
1617
|
+
const clock = clocks[i]!;
|
|
1618
|
+
wallTimes[i] = clock.timestamp.wallTime;
|
|
1619
|
+
logicals[i] = clock.timestamp.logical;
|
|
1620
|
+
metaDatas[i] = properties.meta?.data;
|
|
1621
|
+
payloadDatas[i] ??= properties.encoding.encoder(properties.data[i]!);
|
|
1622
|
+
}
|
|
1623
|
+
const nativePlainChainInput: NativePlainChainInput = {
|
|
1624
|
+
clockId,
|
|
1625
|
+
privateKey,
|
|
1626
|
+
publicKey,
|
|
1627
|
+
wallTimes,
|
|
1628
|
+
logicals,
|
|
1629
|
+
gid: gid!,
|
|
1630
|
+
initialNext: nextHashes,
|
|
1631
|
+
type: entryType,
|
|
1632
|
+
metaDatas,
|
|
1633
|
+
payloadDatas,
|
|
1634
|
+
};
|
|
1635
|
+
const nativeCommit =
|
|
1636
|
+
properties.nativeGraph?.prepareEntryV0PlainChainCommit;
|
|
1637
|
+
const preparedValue = nativeCommit
|
|
1638
|
+
? nativeCommit.call(
|
|
1639
|
+
properties.nativeGraph,
|
|
1640
|
+
nativePlainChainInput,
|
|
1641
|
+
properties.nativeBlockStore,
|
|
1642
|
+
)
|
|
1643
|
+
: undefined;
|
|
1644
|
+
prepared = isPromiseLike(preparedValue)
|
|
1645
|
+
? await preparedValue
|
|
1646
|
+
: preparedValue;
|
|
1647
|
+
if (prepared) {
|
|
1648
|
+
nativeBlocksCommitted = true;
|
|
1649
|
+
nativeGraphUpdated = true;
|
|
1650
|
+
} else {
|
|
1651
|
+
const nativePrepareAndPut =
|
|
1652
|
+
properties.nativeGraph?.prepareEntryV0PlainChainAndPut;
|
|
1653
|
+
const preparedFallbackValue = nativePrepareAndPut
|
|
1654
|
+
? nativePrepareAndPut.call(
|
|
1655
|
+
properties.nativeGraph,
|
|
1656
|
+
nativePlainChainInput,
|
|
1657
|
+
)
|
|
1658
|
+
: nativeEncoder.prepareEntryV0PlainChain(nativePlainChainInput);
|
|
1659
|
+
prepared = isPromiseLike(preparedFallbackValue)
|
|
1660
|
+
? await preparedFallbackValue
|
|
1661
|
+
: preparedFallbackValue;
|
|
1662
|
+
nativeGraphUpdated = !!nativePrepareAndPut;
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
const entries: PreparedAppendChain<T>["entries"] = [];
|
|
1667
|
+
const blocks: NonNullable<PreparedAppendChain<T>["blocks"]> = [];
|
|
1668
|
+
const shallowEntries: PreparedAppendChain<T>["shallowEntries"] = [];
|
|
1669
|
+
const appendFacts: NonNullable<PreparedAppendChain<T>["appendFacts"]> = [];
|
|
1670
|
+
const nativeEntries: NonNullable<PreparedAppendChain<T>["nativeEntries"]> =
|
|
1671
|
+
[];
|
|
1672
|
+
if (!payloadDatas) {
|
|
1673
|
+
throw new Error("Expected payload data for prepared append chain");
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
for (let index = 0; index < prepared.length; index++) {
|
|
1677
|
+
const preparedEntry = prepared[index]!;
|
|
1678
|
+
const clock = clocks[index]!;
|
|
1679
|
+
const meta = new Meta({
|
|
1680
|
+
clock,
|
|
1681
|
+
gid: gid!,
|
|
1682
|
+
type: entryType,
|
|
1683
|
+
data: properties.meta?.data,
|
|
1684
|
+
next: preparedEntry.next,
|
|
1685
|
+
});
|
|
1686
|
+
const payloadSize = payloadDatas[index]!.byteLength;
|
|
1687
|
+
const payload =
|
|
1688
|
+
properties.cachePreparedEntries === false
|
|
1689
|
+
? undefined
|
|
1690
|
+
: new Payload<T>({
|
|
1691
|
+
data: payloadDatas[index]!,
|
|
1692
|
+
value: properties.data[index],
|
|
1693
|
+
encoding: properties.encoding,
|
|
1694
|
+
});
|
|
1695
|
+
const signature =
|
|
1696
|
+
properties.cachePreparedEntries === false
|
|
1697
|
+
? undefined
|
|
1698
|
+
: new SignatureWithKey({
|
|
1699
|
+
signature: preparedEntry.signature!,
|
|
1700
|
+
publicKey: properties.identity.publicKey,
|
|
1701
|
+
prehash: 0,
|
|
1702
|
+
});
|
|
1703
|
+
const entry = new EntryV0<T>({
|
|
1704
|
+
meta: new DecryptedThing({
|
|
1705
|
+
data: preparedEntry.metaBytes,
|
|
1706
|
+
value: meta,
|
|
1707
|
+
}),
|
|
1708
|
+
payload: new DecryptedThing({
|
|
1709
|
+
data: preparedEntry.payloadBytes,
|
|
1710
|
+
value: payload,
|
|
1711
|
+
}),
|
|
1712
|
+
signatures: new Signatures({
|
|
1713
|
+
signatures: [
|
|
1714
|
+
new DecryptedThing({
|
|
1715
|
+
data: preparedEntry.signatureBytes,
|
|
1716
|
+
value: signature,
|
|
1717
|
+
}),
|
|
1718
|
+
],
|
|
1719
|
+
}),
|
|
1720
|
+
createdLocally: true,
|
|
1721
|
+
});
|
|
1722
|
+
const preparedBlock =
|
|
1723
|
+
preparedEntry.bytes && !nativeBlocksCommitted
|
|
1724
|
+
? Entry.preparedBlockFromBytes(preparedEntry.bytes, preparedEntry.cid)
|
|
1725
|
+
: undefined;
|
|
1726
|
+
if (properties.cachePreparedEntries === false) {
|
|
1727
|
+
entry.hash = preparedEntry.cid;
|
|
1728
|
+
entry.size = preparedEntry.byteLength;
|
|
1729
|
+
} else {
|
|
1730
|
+
if (!preparedEntry.bytes) {
|
|
1731
|
+
throw new Error("Missing prepared entry bytes");
|
|
1732
|
+
}
|
|
1733
|
+
entry.hash = Entry.prepareMultihashBytes(
|
|
1734
|
+
entry,
|
|
1735
|
+
preparedEntry.bytes,
|
|
1736
|
+
preparedEntry.cid,
|
|
1737
|
+
);
|
|
1738
|
+
}
|
|
1739
|
+
entry._hashDigestBytes = preparedEntry.hashDigestBytes;
|
|
1740
|
+
const shallowEntry = new ShallowEntry({
|
|
1741
|
+
hash: entry.hash,
|
|
1742
|
+
payloadSize,
|
|
1743
|
+
head: index === prepared.length - 1,
|
|
1744
|
+
meta: new ShallowMeta({
|
|
1745
|
+
gid: gid!,
|
|
1746
|
+
data: properties.meta?.data,
|
|
1747
|
+
clock,
|
|
1748
|
+
next: preparedEntry.next,
|
|
1749
|
+
type: entryType,
|
|
1750
|
+
}),
|
|
1751
|
+
});
|
|
1752
|
+
const nativeEntry =
|
|
1753
|
+
nativeGraphUpdated && properties.cachePreparedEntries === false
|
|
1754
|
+
? undefined
|
|
1755
|
+
: {
|
|
1756
|
+
hash: entry.hash,
|
|
1757
|
+
gid: gid!,
|
|
1758
|
+
next: preparedEntry.next,
|
|
1759
|
+
type: entryType,
|
|
1760
|
+
head: index === prepared.length - 1,
|
|
1761
|
+
payloadSize,
|
|
1762
|
+
data: properties.meta?.data,
|
|
1763
|
+
clock: {
|
|
1764
|
+
timestamp: {
|
|
1765
|
+
wallTime: clock.timestamp.wallTime,
|
|
1766
|
+
logical: clock.timestamp.logical,
|
|
1767
|
+
},
|
|
1768
|
+
},
|
|
1769
|
+
};
|
|
1770
|
+
if (properties.cachePreparedEntries !== false) {
|
|
1771
|
+
Entry.prepareShallowEntry(entry, shallowEntry);
|
|
1772
|
+
Entry.prepareNativeLogEntry(entry, nativeEntry!);
|
|
1773
|
+
}
|
|
1774
|
+
if (properties.cachePreparedEntries !== false) {
|
|
1775
|
+
entry.init({ encoding: properties.encoding });
|
|
1776
|
+
}
|
|
1777
|
+
entries.push(entry);
|
|
1778
|
+
if (preparedBlock) {
|
|
1779
|
+
blocks.push(preparedBlock);
|
|
1780
|
+
}
|
|
1781
|
+
shallowEntries.push(shallowEntry);
|
|
1782
|
+
appendFacts.push({
|
|
1783
|
+
hash: entry.hash,
|
|
1784
|
+
gid: gid!,
|
|
1785
|
+
next: preparedEntry.next,
|
|
1786
|
+
wallTime: clock.timestamp.wallTime,
|
|
1787
|
+
logical: clock.timestamp.logical,
|
|
1788
|
+
clockId: clock.id,
|
|
1789
|
+
type: entryType,
|
|
1790
|
+
metaData: properties.meta?.data,
|
|
1791
|
+
payloadSize,
|
|
1792
|
+
metaBytes: preparedEntry.metaBytes,
|
|
1793
|
+
hashDigestBytes: preparedEntry.hashDigestBytes,
|
|
1794
|
+
});
|
|
1795
|
+
if (nativeEntry) {
|
|
1796
|
+
nativeEntries.push(nativeEntry);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
return {
|
|
1800
|
+
entries,
|
|
1801
|
+
blocks: blocks.length > 0 ? blocks : undefined,
|
|
1802
|
+
shallowEntries,
|
|
1803
|
+
appendFacts,
|
|
1804
|
+
nativeEntries,
|
|
1805
|
+
nativeGraphUpdated,
|
|
1806
|
+
nativeBlocksCommitted,
|
|
1807
|
+
};
|
|
399
1808
|
}
|
|
400
1809
|
|
|
401
1810
|
static async create<T>(properties: {
|
|
@@ -412,6 +1821,7 @@ export class EntryV0<T>
|
|
|
412
1821
|
encoding?: Encoding<T>;
|
|
413
1822
|
canAppend?: CanAppend<T>;
|
|
414
1823
|
encryption?: EntryEncryption;
|
|
1824
|
+
deferStore?: boolean;
|
|
415
1825
|
identity: Identity;
|
|
416
1826
|
signers?: ((
|
|
417
1827
|
data: Uint8Array,
|
|
@@ -505,12 +1915,13 @@ export class EntryV0<T>
|
|
|
505
1915
|
properties.meta?.gid ||
|
|
506
1916
|
(await EntryV0.createGid(properties.meta?.gidSeed));
|
|
507
1917
|
}
|
|
1918
|
+
const entryType = properties.meta?.type ?? EntryType.APPEND;
|
|
508
1919
|
|
|
509
1920
|
const metadataEncrypted = await maybeEncrypt(
|
|
510
1921
|
new Meta({
|
|
511
1922
|
clock,
|
|
512
1923
|
gid: gid!,
|
|
513
|
-
type:
|
|
1924
|
+
type: entryType,
|
|
514
1925
|
data: properties.meta?.data,
|
|
515
1926
|
next: nextHashes,
|
|
516
1927
|
}),
|
|
@@ -523,6 +1934,21 @@ export class EntryV0<T>
|
|
|
523
1934
|
properties.encryption?.keypair,
|
|
524
1935
|
properties.encryption?.receiver.payload,
|
|
525
1936
|
);
|
|
1937
|
+
const nativeEncoder = properties.encryption
|
|
1938
|
+
? undefined
|
|
1939
|
+
: await loadNativeEntryV0Encoder();
|
|
1940
|
+
const nativePlainInput = nativeEncoder
|
|
1941
|
+
? {
|
|
1942
|
+
clockId: clock.id,
|
|
1943
|
+
wallTime: clock.timestamp.wallTime,
|
|
1944
|
+
logical: clock.timestamp.logical,
|
|
1945
|
+
gid: gid!,
|
|
1946
|
+
next: nextHashes,
|
|
1947
|
+
type: entryType,
|
|
1948
|
+
metaData: properties.meta?.data,
|
|
1949
|
+
payloadData: payloadToSave.data,
|
|
1950
|
+
}
|
|
1951
|
+
: undefined;
|
|
526
1952
|
|
|
527
1953
|
// Sign id, encrypted payload, clock, nexts, refs
|
|
528
1954
|
const entry: EntryV0<T> = new EntryV0<T>({
|
|
@@ -532,14 +1958,17 @@ export class EntryV0<T>
|
|
|
532
1958
|
createdLocally: true,
|
|
533
1959
|
});
|
|
534
1960
|
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
1961
|
+
const signableBytes =
|
|
1962
|
+
nativeEncoder && nativePlainInput
|
|
1963
|
+
? await nativeEncoder.encodeEntryV0Signable(nativePlainInput)
|
|
1964
|
+
: entry.getSignableBytes();
|
|
1965
|
+
let signatures = properties.signers
|
|
1966
|
+
? properties.signers.length === 1
|
|
1967
|
+
? [await properties.signers[0]!(signableBytes)]
|
|
1968
|
+
: await Promise.all(
|
|
1969
|
+
properties.signers.map((signer) => signer(signableBytes)),
|
|
1970
|
+
)
|
|
1971
|
+
: [await properties.identity.sign(signableBytes)];
|
|
543
1972
|
signatures = signatures.sort((a, b) => compare(a.signature, b.signature));
|
|
544
1973
|
|
|
545
1974
|
const encryptedSignatures: MaybeEncrypted<SignatureWithKey>[] = [];
|
|
@@ -569,8 +1998,54 @@ export class EntryV0<T>
|
|
|
569
1998
|
throw new AccessError("Not allowed to append");
|
|
570
1999
|
}
|
|
571
2000
|
|
|
2001
|
+
let nativeStorage:
|
|
2002
|
+
| {
|
|
2003
|
+
bytes: Uint8Array;
|
|
2004
|
+
cid: string;
|
|
2005
|
+
}
|
|
2006
|
+
| undefined;
|
|
2007
|
+
if (
|
|
2008
|
+
nativeEncoder &&
|
|
2009
|
+
nativePlainInput &&
|
|
2010
|
+
!properties.canAppend &&
|
|
2011
|
+
signatures.length === 1 &&
|
|
2012
|
+
signatures[0]!.publicKey instanceof Ed25519PublicKey
|
|
2013
|
+
) {
|
|
2014
|
+
const storageInput = {
|
|
2015
|
+
...nativePlainInput,
|
|
2016
|
+
signature: signatures[0]!.signature,
|
|
2017
|
+
signaturePublicKey: signatures[0]!.publicKey.publicKey,
|
|
2018
|
+
prehash: signatures[0]!.prehash,
|
|
2019
|
+
};
|
|
2020
|
+
nativeStorage = nativeEncoder.encodeEntryV0StorageWithCid
|
|
2021
|
+
? await nativeEncoder.encodeEntryV0StorageWithCid(storageInput)
|
|
2022
|
+
: await (async () => {
|
|
2023
|
+
const storageBytes =
|
|
2024
|
+
await nativeEncoder.encodeEntryV0Storage(storageInput);
|
|
2025
|
+
return {
|
|
2026
|
+
bytes: storageBytes,
|
|
2027
|
+
cid: await nativeEncoder.calculateRawCidV1(storageBytes),
|
|
2028
|
+
};
|
|
2029
|
+
})();
|
|
2030
|
+
}
|
|
2031
|
+
|
|
572
2032
|
// Append hash
|
|
573
|
-
entry.hash =
|
|
2033
|
+
entry.hash = properties.deferStore
|
|
2034
|
+
? nativeStorage
|
|
2035
|
+
? Entry.prepareMultihashBytes(
|
|
2036
|
+
entry,
|
|
2037
|
+
nativeStorage.bytes,
|
|
2038
|
+
nativeStorage.cid,
|
|
2039
|
+
)
|
|
2040
|
+
: await Entry.prepareMultihash(entry)
|
|
2041
|
+
: nativeStorage
|
|
2042
|
+
? await Entry.toMultihashBytes(
|
|
2043
|
+
properties.store,
|
|
2044
|
+
entry,
|
|
2045
|
+
nativeStorage.bytes,
|
|
2046
|
+
nativeStorage.cid,
|
|
2047
|
+
)
|
|
2048
|
+
: await Entry.toMultihash(properties.store, entry);
|
|
574
2049
|
|
|
575
2050
|
entry.init({ encoding: properties.encoding });
|
|
576
2051
|
|