cojson 0.17.9 → 0.17.10

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.
Files changed (65) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +7 -0
  3. package/dist/coValueCore/SessionMap.d.ts +44 -0
  4. package/dist/coValueCore/SessionMap.d.ts.map +1 -0
  5. package/dist/coValueCore/SessionMap.js +118 -0
  6. package/dist/coValueCore/SessionMap.js.map +1 -0
  7. package/dist/coValueCore/coValueCore.d.ts +1 -0
  8. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  9. package/dist/coValueCore/coValueCore.js +40 -61
  10. package/dist/coValueCore/coValueCore.js.map +1 -1
  11. package/dist/coValueCore/verifiedState.d.ts +14 -18
  12. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  13. package/dist/coValueCore/verifiedState.js +23 -86
  14. package/dist/coValueCore/verifiedState.js.map +1 -1
  15. package/dist/coValues/account.d.ts +4 -0
  16. package/dist/coValues/account.d.ts.map +1 -1
  17. package/dist/coValues/account.js +24 -4
  18. package/dist/coValues/account.js.map +1 -1
  19. package/dist/crypto/PureJSCrypto.d.ts +31 -3
  20. package/dist/crypto/PureJSCrypto.d.ts.map +1 -1
  21. package/dist/crypto/PureJSCrypto.js +112 -0
  22. package/dist/crypto/PureJSCrypto.js.map +1 -1
  23. package/dist/crypto/WasmCrypto.d.ts +23 -4
  24. package/dist/crypto/WasmCrypto.d.ts.map +1 -1
  25. package/dist/crypto/WasmCrypto.js +44 -2
  26. package/dist/crypto/WasmCrypto.js.map +1 -1
  27. package/dist/crypto/crypto.d.ts +17 -1
  28. package/dist/crypto/crypto.d.ts.map +1 -1
  29. package/dist/crypto/crypto.js.map +1 -1
  30. package/dist/localNode.d.ts +1 -0
  31. package/dist/localNode.d.ts.map +1 -1
  32. package/dist/localNode.js +10 -5
  33. package/dist/localNode.js.map +1 -1
  34. package/dist/tests/PureJSCrypto.test.d.ts +2 -0
  35. package/dist/tests/PureJSCrypto.test.d.ts.map +1 -0
  36. package/dist/tests/PureJSCrypto.test.js +88 -0
  37. package/dist/tests/PureJSCrypto.test.js.map +1 -0
  38. package/dist/tests/WasmCrypto.test.d.ts +2 -0
  39. package/dist/tests/WasmCrypto.test.d.ts.map +1 -0
  40. package/dist/tests/WasmCrypto.test.js +88 -0
  41. package/dist/tests/WasmCrypto.test.js.map +1 -0
  42. package/dist/tests/coValueCore.test.js +62 -187
  43. package/dist/tests/coValueCore.test.js.map +1 -1
  44. package/dist/tests/coreWasm.test.d.ts +2 -0
  45. package/dist/tests/coreWasm.test.d.ts.map +1 -0
  46. package/dist/tests/coreWasm.test.js +80 -0
  47. package/dist/tests/coreWasm.test.js.map +1 -0
  48. package/dist/tests/testUtils.d.ts +3 -0
  49. package/dist/tests/testUtils.d.ts.map +1 -1
  50. package/dist/tests/testUtils.js +4 -1
  51. package/dist/tests/testUtils.js.map +1 -1
  52. package/package.json +3 -3
  53. package/src/coValueCore/SessionMap.ts +230 -0
  54. package/src/coValueCore/coValueCore.ts +66 -91
  55. package/src/coValueCore/verifiedState.ts +60 -129
  56. package/src/coValues/account.ts +28 -4
  57. package/src/crypto/PureJSCrypto.ts +202 -2
  58. package/src/crypto/WasmCrypto.ts +95 -4
  59. package/src/crypto/crypto.ts +38 -1
  60. package/src/localNode.ts +18 -10
  61. package/src/tests/PureJSCrypto.test.ts +130 -0
  62. package/src/tests/WasmCrypto.test.ts +130 -0
  63. package/src/tests/coValueCore.test.ts +84 -292
  64. package/src/tests/coreWasm.test.ts +142 -0
  65. package/src/tests/testUtils.ts +9 -1
@@ -10,6 +10,7 @@ import {
10
10
  Encrypted,
11
11
  Hash,
12
12
  KeyID,
13
+ KeySecret,
13
14
  Signature,
14
15
  SignerID,
15
16
  StreamingHash,
@@ -21,6 +22,8 @@ import { PermissionsDef as RulesetDef } from "../permissions.js";
21
22
  import { CoValueKnownState, NewContentMessage } from "../sync.js";
22
23
  import { InvalidHashError, InvalidSignatureError } from "./coValueCore.js";
23
24
  import { TryAddTransactionsError } from "./coValueCore.js";
25
+ import { SessionLog, SessionMap } from "./SessionMap.js";
26
+ import { ControlledAccountOrAgent } from "../coValues/account.js";
24
27
 
25
28
  export type CoValueHeader = {
26
29
  type: AnyRawCoValue["type"];
@@ -48,20 +51,11 @@ export type TrustingTransaction = {
48
51
 
49
52
  export type Transaction = PrivateTransaction | TrustingTransaction;
50
53
 
51
- type SessionLog = {
52
- readonly transactions: Transaction[];
53
- streamingHash?: StreamingHash;
54
- readonly signatureAfter: { [txIdx: number]: Signature | undefined };
55
- lastSignature: Signature;
56
- };
57
-
58
- export type ValidatedSessions = Map<SessionID, SessionLog>;
59
-
60
54
  export class VerifiedState {
61
55
  readonly id: RawCoID;
62
56
  readonly crypto: CryptoProvider;
63
57
  readonly header: CoValueHeader;
64
- readonly sessions: ValidatedSessions;
58
+ readonly sessions: SessionMap;
65
59
  private _cachedKnownState?: CoValueKnownState;
66
60
  private _cachedNewContentSinceEmpty: NewContentMessage[] | undefined;
67
61
  private streamingKnownState?: CoValueKnownState["sessions"];
@@ -71,35 +65,25 @@ export class VerifiedState {
71
65
  id: RawCoID,
72
66
  crypto: CryptoProvider,
73
67
  header: CoValueHeader,
74
- sessions: ValidatedSessions,
68
+ sessions?: SessionMap,
75
69
  streamingKnownState?: CoValueKnownState["sessions"],
76
70
  ) {
77
71
  this.id = id;
78
72
  this.crypto = crypto;
79
73
  this.header = header;
80
- this.sessions = sessions;
74
+ this.sessions = sessions ?? new SessionMap(id, crypto);
81
75
  this.streamingKnownState = streamingKnownState
82
76
  ? { ...streamingKnownState }
83
77
  : undefined;
84
78
  }
85
79
 
86
80
  clone(): VerifiedState {
87
- // do a deep clone, including the sessions
88
- const clonedSessions = new Map();
89
- for (let [sessionID, sessionLog] of this.sessions) {
90
- clonedSessions.set(sessionID, {
91
- lastSignature: sessionLog.lastSignature,
92
- streamingHash: sessionLog.streamingHash?.clone(),
93
- signatureAfter: { ...sessionLog.signatureAfter },
94
- transactions: sessionLog.transactions.slice(),
95
- } satisfies SessionLog);
96
- }
97
81
  return new VerifiedState(
98
82
  this.id,
99
83
  this.crypto,
100
84
  this.header,
101
- clonedSessions,
102
- this.streamingKnownState,
85
+ this.sessions.clone(),
86
+ this.streamingKnownState ? { ...this.streamingKnownState } : undefined,
103
87
  );
104
88
  }
105
89
 
@@ -112,130 +96,69 @@ export class VerifiedState {
112
96
  skipVerify: boolean = false,
113
97
  givenNewStreamingHash?: StreamingHash,
114
98
  ): Result<true, TryAddTransactionsError> {
115
- if (skipVerify === true) {
116
- this.doAddTransactions(
117
- sessionID,
118
- newTransactions,
119
- newSignature,
120
- givenNewStreamingHash,
121
- );
122
- } else {
123
- const { expectedNewHash, newStreamingHash } = this.expectedNewHashAfter(
124
- sessionID,
125
- newTransactions,
126
- );
127
-
128
- if (givenExpectedNewHash && givenExpectedNewHash !== expectedNewHash) {
129
- return err({
130
- type: "InvalidHash",
131
- id: this.id,
132
- expectedNewHash,
133
- givenExpectedNewHash,
134
- } satisfies InvalidHashError);
135
- }
136
-
137
- if (!this.crypto.verify(newSignature, expectedNewHash, signerID)) {
138
- return err({
139
- type: "InvalidSignature",
140
- id: this.id,
141
- newSignature,
142
- sessionID,
143
- signerID,
144
- } satisfies InvalidSignatureError);
145
- }
99
+ const result = this.sessions.addTransaction(
100
+ sessionID,
101
+ signerID,
102
+ newTransactions,
103
+ newSignature,
104
+ skipVerify,
105
+ );
146
106
 
147
- this.doAddTransactions(
148
- sessionID,
149
- newTransactions,
150
- newSignature,
151
- newStreamingHash,
152
- );
107
+ if (result.isOk()) {
108
+ this._cachedNewContentSinceEmpty = undefined;
109
+ this._cachedKnownState = undefined;
153
110
  }
154
111
 
155
- return ok(true as const);
112
+ return result;
156
113
  }
157
114
 
158
- getLastSignatureCheckpoint(sessionID: SessionID): number {
159
- const sessionLog = this.sessions.get(sessionID);
115
+ makeNewTrustingTransaction(
116
+ sessionID: SessionID,
117
+ signerAgent: ControlledAccountOrAgent,
118
+ changes: JsonValue[],
119
+ ) {
120
+ const result = this.sessions.makeNewTrustingTransaction(
121
+ sessionID,
122
+ signerAgent,
123
+ changes,
124
+ );
160
125
 
161
- if (!sessionLog?.signatureAfter) return -1;
126
+ this._cachedNewContentSinceEmpty = undefined;
127
+ this._cachedKnownState = undefined;
162
128
 
163
- return Object.keys(sessionLog.signatureAfter).reduce(
164
- (max, idx) => Math.max(max, parseInt(idx)),
165
- -1,
166
- );
129
+ return result;
167
130
  }
168
131
 
169
- private doAddTransactions(
132
+ makeNewPrivateTransaction(
170
133
  sessionID: SessionID,
171
- newTransactions: Transaction[],
172
- newSignature: Signature,
173
- newStreamingHash?: StreamingHash,
134
+ signerAgent: ControlledAccountOrAgent,
135
+ changes: JsonValue[],
136
+ keyID: KeyID,
137
+ keySecret: KeySecret,
174
138
  ) {
175
- const sessionLog = this.sessions.get(sessionID);
176
- const transactions = sessionLog?.transactions ?? [];
177
-
178
- for (const tx of newTransactions) {
179
- transactions.push(tx);
180
- }
181
-
182
- const signatureAfter = sessionLog?.signatureAfter ?? {};
183
- const lastInbetweenSignatureIdx =
184
- this.getLastSignatureCheckpoint(sessionID);
185
-
186
- const sizeOfTxsSinceLastInbetweenSignature = transactions
187
- .slice(lastInbetweenSignatureIdx + 1)
188
- .reduce((sum, tx) => sum + getTransactionSize(tx), 0);
189
-
190
- if (exceedsRecommendedSize(sizeOfTxsSinceLastInbetweenSignature)) {
191
- signatureAfter[transactions.length - 1] = newSignature;
192
- }
193
-
194
- this.sessions.set(sessionID, {
195
- transactions,
196
- streamingHash: newStreamingHash,
197
- lastSignature: newSignature,
198
- signatureAfter: signatureAfter,
199
- });
139
+ const result = this.sessions.makeNewPrivateTransaction(
140
+ sessionID,
141
+ signerAgent,
142
+ changes,
143
+ keyID,
144
+ keySecret,
145
+ );
200
146
 
201
147
  this._cachedNewContentSinceEmpty = undefined;
202
148
  this._cachedKnownState = undefined;
149
+
150
+ return result;
203
151
  }
204
152
 
205
- expectedNewHashAfter(
206
- sessionID: SessionID,
207
- newTransactions: Transaction[],
208
- ): { expectedNewHash: Hash; newStreamingHash: StreamingHash } {
153
+ getLastSignatureCheckpoint(sessionID: SessionID): number {
209
154
  const sessionLog = this.sessions.get(sessionID);
210
155
 
211
- if (!sessionLog?.streamingHash) {
212
- const streamingHash = new StreamingHash(this.crypto);
213
- const oldTransactions = sessionLog?.transactions ?? [];
214
-
215
- for (const transaction of oldTransactions) {
216
- streamingHash.update(transaction);
217
- }
218
-
219
- for (const transaction of newTransactions) {
220
- streamingHash.update(transaction);
221
- }
222
-
223
- return {
224
- expectedNewHash: streamingHash.digest(),
225
- newStreamingHash: streamingHash,
226
- };
227
- }
228
-
229
- const streamingHash = sessionLog.streamingHash.clone();
230
-
231
- for (const transaction of newTransactions) {
232
- streamingHash.update(transaction);
233
- }
156
+ if (!sessionLog?.signatureAfter) return -1;
234
157
 
235
- return {
236
- expectedNewHash: streamingHash.digest(),
237
- newStreamingHash: streamingHash,
238
- };
158
+ return Object.keys(sessionLog.signatureAfter).reduce(
159
+ (max, idx) => Math.max(max, parseInt(idx)),
160
+ -1,
161
+ );
239
162
  }
240
163
 
241
164
  newContentSince(
@@ -424,6 +347,14 @@ export class VerifiedState {
424
347
  sessions,
425
348
  };
426
349
  }
350
+
351
+ decryptTransaction(
352
+ sessionID: SessionID,
353
+ txIndex: number,
354
+ keySecret: KeySecret,
355
+ ): JsonValue[] | undefined {
356
+ return this.sessions.decryptTransaction(sessionID, txIndex, keySecret);
357
+ }
427
358
  }
428
359
 
429
360
  function getNextKnownSignatureIdx(
@@ -92,6 +92,10 @@ export class ControlledAccount implements ControlledAccountOrAgent {
92
92
  account: RawAccount<AccountMeta>;
93
93
  agentSecret: AgentSecret;
94
94
  _cachedCurrentAgentID: AgentID | undefined;
95
+ _cachedCurrentSignerID: SignerID | undefined;
96
+ _cachedCurrentSignerSecret: SignerSecret | undefined;
97
+ _cachedCurrentSealerID: SealerID | undefined;
98
+ _cachedCurrentSealerSecret: SealerSecret | undefined;
95
99
  crypto: CryptoProvider;
96
100
 
97
101
  constructor(account: RawAccount<AccountMeta>, agentSecret: AgentSecret) {
@@ -114,19 +118,39 @@ export class ControlledAccount implements ControlledAccountOrAgent {
114
118
  }
115
119
 
116
120
  currentSignerID() {
117
- return this.crypto.getAgentSignerID(this.currentAgentID());
121
+ if (this._cachedCurrentSignerID) {
122
+ return this._cachedCurrentSignerID;
123
+ }
124
+ const signerID = this.crypto.getAgentSignerID(this.currentAgentID());
125
+ this._cachedCurrentSignerID = signerID;
126
+ return signerID;
118
127
  }
119
128
 
120
129
  currentSignerSecret(): SignerSecret {
121
- return this.crypto.getAgentSignerSecret(this.agentSecret);
130
+ if (this._cachedCurrentSignerSecret) {
131
+ return this._cachedCurrentSignerSecret;
132
+ }
133
+ const signerSecret = this.crypto.getAgentSignerSecret(this.agentSecret);
134
+ this._cachedCurrentSignerSecret = signerSecret;
135
+ return signerSecret;
122
136
  }
123
137
 
124
138
  currentSealerID() {
125
- return this.crypto.getAgentSealerID(this.currentAgentID());
139
+ if (this._cachedCurrentSealerID) {
140
+ return this._cachedCurrentSealerID;
141
+ }
142
+ const sealerID = this.crypto.getAgentSealerID(this.currentAgentID());
143
+ this._cachedCurrentSealerID = sealerID;
144
+ return sealerID;
126
145
  }
127
146
 
128
147
  currentSealerSecret(): SealerSecret {
129
- return this.crypto.getAgentSealerSecret(this.agentSecret);
148
+ if (this._cachedCurrentSealerSecret) {
149
+ return this._cachedCurrentSealerSecret;
150
+ }
151
+ const sealerSecret = this.crypto.getAgentSealerSecret(this.agentSecret);
152
+ this._cachedCurrentSealerSecret = sealerSecret;
153
+ return sealerSecret;
130
154
  }
131
155
  }
132
156
 
@@ -3,23 +3,32 @@ import { ed25519, x25519 } from "@noble/curves/ed25519";
3
3
  import { blake3 } from "@noble/hashes/blake3";
4
4
  import { base58 } from "@scure/base";
5
5
  import { base64URLtoBytes, bytesToBase64url } from "../base64url.js";
6
- import { RawCoID, TransactionID } from "../ids.js";
6
+ import {
7
+ PrivateTransaction,
8
+ Transaction,
9
+ TrustingTransaction,
10
+ } from "../coValueCore/verifiedState.js";
11
+ import { RawCoID, SessionID, TransactionID } from "../ids.js";
7
12
  import { Stringified, stableStringify } from "../jsonStringify.js";
8
13
  import { JsonValue } from "../jsonValue.js";
9
14
  import { logger } from "../logger.js";
10
15
  import {
11
16
  CryptoProvider,
12
17
  Encrypted,
18
+ KeyID,
13
19
  KeySecret,
14
20
  Sealed,
15
21
  SealerID,
16
22
  SealerSecret,
23
+ SessionLogImpl,
17
24
  Signature,
18
25
  SignerID,
19
26
  SignerSecret,
27
+ StreamingHash,
20
28
  textDecoder,
21
29
  textEncoder,
22
30
  } from "./crypto.js";
31
+ import { ControlledAccountOrAgent } from "../coValues/account.js";
23
32
 
24
33
  type Blake3State = ReturnType<typeof blake3.create>;
25
34
 
@@ -67,7 +76,7 @@ export class PureJSCrypto extends CryptoProvider<Blake3State> {
67
76
  return this.blake3HashOnce(input).slice(0, 24);
68
77
  }
69
78
 
70
- protected generateJsonNonce(material: JsonValue): Uint8Array {
79
+ generateJsonNonce(material: JsonValue): Uint8Array {
71
80
  return this.generateNonce(textEncoder.encode(stableStringify(material)));
72
81
  }
73
82
 
@@ -199,4 +208,195 @@ export class PureJSCrypto extends CryptoProvider<Blake3State> {
199
208
  return undefined;
200
209
  }
201
210
  }
211
+
212
+ createSessionLog(
213
+ coID: RawCoID,
214
+ sessionID: SessionID,
215
+ signerID: SignerID,
216
+ ): SessionLogImpl {
217
+ return new PureJSSessionLog(coID, sessionID, signerID, this);
218
+ }
219
+ }
220
+
221
+ export class PureJSSessionLog implements SessionLogImpl {
222
+ transactions: string[] = [];
223
+ lastSignature: Signature | undefined;
224
+ streamingHash: Blake3State;
225
+
226
+ constructor(
227
+ private readonly coID: RawCoID,
228
+ private readonly sessionID: SessionID,
229
+ private readonly signerID: SignerID,
230
+ private readonly crypto: PureJSCrypto,
231
+ ) {
232
+ this.streamingHash = this.crypto.emptyBlake3State();
233
+ }
234
+
235
+ clone(): SessionLogImpl {
236
+ const newLog = new PureJSSessionLog(
237
+ this.coID,
238
+ this.sessionID,
239
+ this.signerID,
240
+ this.crypto,
241
+ );
242
+ newLog.transactions = this.transactions.slice();
243
+ newLog.lastSignature = this.lastSignature;
244
+ newLog.streamingHash = this.crypto.cloneBlake3State(this.streamingHash);
245
+ return newLog;
246
+ }
247
+
248
+ tryAdd(
249
+ transactions: Transaction[],
250
+ newSignature: Signature,
251
+ skipVerify: boolean,
252
+ ): void {
253
+ this.internalTryAdd(
254
+ transactions.map((tx) => stableStringify(tx)),
255
+ newSignature,
256
+ skipVerify,
257
+ );
258
+ }
259
+
260
+ internalTryAdd(
261
+ transactions: string[],
262
+ newSignature: Signature,
263
+ skipVerify: boolean,
264
+ ) {
265
+ if (!skipVerify) {
266
+ const checkHasher = this.crypto.cloneBlake3State(this.streamingHash);
267
+
268
+ for (const tx of transactions) {
269
+ checkHasher.update(textEncoder.encode(tx));
270
+ }
271
+ const newHash = checkHasher.digest();
272
+ const newHashEncoded = `hash_z${base58.encode(newHash)}`;
273
+
274
+ if (!this.crypto.verify(newSignature, newHashEncoded, this.signerID)) {
275
+ throw new Error("Signature verification failed");
276
+ }
277
+ }
278
+
279
+ for (const tx of transactions) {
280
+ this.crypto.blake3IncrementalUpdate(
281
+ this.streamingHash,
282
+ textEncoder.encode(tx),
283
+ );
284
+ this.transactions.push(tx);
285
+ }
286
+
287
+ this.lastSignature = newSignature;
288
+
289
+ return newSignature;
290
+ }
291
+
292
+ expectedHashAfter(transactionsJson: string[]): string {
293
+ const hasher = this.crypto.cloneBlake3State(this.streamingHash);
294
+ for (const tx of transactionsJson) {
295
+ hasher.update(textEncoder.encode(tx));
296
+ }
297
+ const newHash = hasher.digest();
298
+ return `hash_z${base58.encode(newHash)}`;
299
+ }
300
+
301
+ internalAddNewTransaction(
302
+ transaction: string,
303
+ signerAgent: ControlledAccountOrAgent,
304
+ ) {
305
+ this.crypto.blake3IncrementalUpdate(
306
+ this.streamingHash,
307
+ textEncoder.encode(transaction),
308
+ );
309
+ const newHash = this.crypto.blake3DigestForState(this.streamingHash);
310
+ const newHashEncoded = `hash_z${base58.encode(newHash)}`;
311
+ const signature = this.crypto.sign(
312
+ signerAgent.currentSignerSecret(),
313
+ newHashEncoded,
314
+ );
315
+ this.transactions.push(transaction);
316
+ this.lastSignature = signature;
317
+
318
+ return signature;
319
+ }
320
+
321
+ addNewPrivateTransaction(
322
+ signerAgent: ControlledAccountOrAgent,
323
+ changes: JsonValue[],
324
+ keyID: KeyID,
325
+ keySecret: KeySecret,
326
+ madeAt: number,
327
+ ): { signature: Signature; transaction: PrivateTransaction } {
328
+ const encryptedChanges = this.crypto.encrypt(changes, keySecret, {
329
+ in: this.coID,
330
+ tx: { sessionID: this.sessionID, txIndex: this.transactions.length },
331
+ });
332
+ const tx = {
333
+ encryptedChanges: encryptedChanges,
334
+ madeAt: madeAt,
335
+ privacy: "private",
336
+ keyUsed: keyID,
337
+ } satisfies Transaction;
338
+ const signature = this.internalAddNewTransaction(
339
+ stableStringify(tx),
340
+ signerAgent,
341
+ );
342
+ return {
343
+ signature: signature as Signature,
344
+ transaction: tx,
345
+ };
346
+ }
347
+
348
+ addNewTrustingTransaction(
349
+ signerAgent: ControlledAccountOrAgent,
350
+ changes: JsonValue[],
351
+ madeAt: number,
352
+ ): { signature: Signature; transaction: TrustingTransaction } {
353
+ const tx = {
354
+ changes: stableStringify(changes),
355
+ madeAt: madeAt,
356
+ privacy: "trusting",
357
+ } satisfies Transaction;
358
+ const signature = this.internalAddNewTransaction(
359
+ stableStringify(tx),
360
+ signerAgent,
361
+ );
362
+ return {
363
+ signature: signature as Signature,
364
+ transaction: tx,
365
+ };
366
+ }
367
+
368
+ decryptNextTransactionChangesJson(
369
+ txIndex: number,
370
+ keySecret: KeySecret,
371
+ ): string {
372
+ const txJson = this.transactions[txIndex];
373
+ if (!txJson) {
374
+ throw new Error("Transaction not found");
375
+ }
376
+ const tx = JSON.parse(txJson) as Transaction;
377
+ if (tx.privacy === "private") {
378
+ const nOnceMaterial = {
379
+ in: this.coID,
380
+ tx: { sessionID: this.sessionID, txIndex: txIndex },
381
+ };
382
+
383
+ const nOnce = this.crypto.generateJsonNonce(nOnceMaterial);
384
+
385
+ const ciphertext = base64URLtoBytes(
386
+ tx.encryptedChanges.substring("encrypted_U".length),
387
+ );
388
+ const keySecretBytes = base58.decode(
389
+ keySecret.substring("keySecret_z".length),
390
+ );
391
+ const plaintext = xsalsa20(keySecretBytes, nOnce, ciphertext);
392
+
393
+ return textDecoder.decode(plaintext);
394
+ } else {
395
+ return tx.changes;
396
+ }
397
+ }
398
+
399
+ free(): void {
400
+ // no-op
401
+ }
202
402
  }
@@ -1,4 +1,6 @@
1
1
  import {
2
+ SessionLog,
3
+ initialize,
2
4
  Blake3Hasher,
3
5
  blake3_empty_state,
4
6
  blake3_hash_once,
@@ -7,16 +9,15 @@ import {
7
9
  encrypt,
8
10
  get_sealer_id,
9
11
  get_signer_id,
10
- initialize,
11
12
  new_ed25519_signing_key,
12
13
  new_x25519_private_key,
13
14
  seal,
14
15
  sign,
15
16
  unseal,
16
17
  verify,
17
- } from "jazz-crypto-rs";
18
+ } from "cojson-core-wasm";
18
19
  import { base64URLtoBytes, bytesToBase64url } from "../base64url.js";
19
- import { RawCoID, TransactionID } from "../ids.js";
20
+ import { RawCoID, SessionID, TransactionID } from "../ids.js";
20
21
  import { Stringified, stableStringify } from "../jsonStringify.js";
21
22
  import { JsonValue } from "../jsonValue.js";
22
23
  import { logger } from "../logger.js";
@@ -24,6 +25,8 @@ import { PureJSCrypto } from "./PureJSCrypto.js";
24
25
  import {
25
26
  CryptoProvider,
26
27
  Encrypted,
28
+ Hash,
29
+ KeyID,
27
30
  KeySecret,
28
31
  Sealed,
29
32
  SealerID,
@@ -34,11 +37,17 @@ import {
34
37
  textDecoder,
35
38
  textEncoder,
36
39
  } from "./crypto.js";
40
+ import { ControlledAccountOrAgent } from "../coValues/account.js";
41
+ import {
42
+ PrivateTransaction,
43
+ Transaction,
44
+ TrustingTransaction,
45
+ } from "../coValueCore/verifiedState.js";
37
46
 
38
47
  type Blake3State = Blake3Hasher;
39
48
 
40
49
  /**
41
- * WebAssembly implementation of the CryptoProvider interface using jazz-crypto-rs.
50
+ * WebAssembly implementation of the CryptoProvider interface using cojson-core-wasm.
42
51
  * This provides the primary implementation using WebAssembly for optimal performance, offering:
43
52
  * - Signing/verifying (Ed25519)
44
53
  * - Encryption/decryption (XSalsa20)
@@ -195,4 +204,86 @@ export class WasmCrypto extends CryptoProvider<Blake3State> {
195
204
  return undefined;
196
205
  }
197
206
  }
207
+
208
+ createSessionLog(coID: RawCoID, sessionID: SessionID, signerID: SignerID) {
209
+ return new SessionLogAdapter(new SessionLog(coID, sessionID, signerID));
210
+ }
211
+ }
212
+
213
+ class SessionLogAdapter {
214
+ constructor(private readonly sessionLog: SessionLog) {}
215
+
216
+ tryAdd(
217
+ transactions: Transaction[],
218
+ newSignature: Signature,
219
+ skipVerify: boolean,
220
+ ): void {
221
+ this.sessionLog.tryAdd(
222
+ transactions.map((tx) => stableStringify(tx)),
223
+ newSignature,
224
+ skipVerify,
225
+ );
226
+ }
227
+
228
+ addNewPrivateTransaction(
229
+ signerAgent: ControlledAccountOrAgent,
230
+ changes: JsonValue[],
231
+ keyID: KeyID,
232
+ keySecret: KeySecret,
233
+ madeAt: number,
234
+ ): { signature: Signature; transaction: PrivateTransaction } {
235
+ const output = this.sessionLog.addNewPrivateTransaction(
236
+ stableStringify(changes),
237
+ signerAgent.currentSignerSecret(),
238
+ keySecret,
239
+ keyID,
240
+ madeAt,
241
+ );
242
+ const parsedOutput = JSON.parse(output);
243
+ const transaction: PrivateTransaction = {
244
+ privacy: "private",
245
+ madeAt,
246
+ encryptedChanges: parsedOutput.encrypted_changes,
247
+ keyUsed: keyID,
248
+ };
249
+ return { signature: parsedOutput.signature, transaction };
250
+ }
251
+
252
+ addNewTrustingTransaction(
253
+ signerAgent: ControlledAccountOrAgent,
254
+ changes: JsonValue[],
255
+ madeAt: number,
256
+ ): { signature: Signature; transaction: TrustingTransaction } {
257
+ const stringifiedChanges = stableStringify(changes);
258
+ const output = this.sessionLog.addNewTrustingTransaction(
259
+ stringifiedChanges,
260
+ signerAgent.currentSignerSecret(),
261
+ madeAt,
262
+ );
263
+ const transaction: TrustingTransaction = {
264
+ privacy: "trusting",
265
+ madeAt,
266
+ changes: stringifiedChanges,
267
+ };
268
+ return { signature: output as Signature, transaction };
269
+ }
270
+
271
+ decryptNextTransactionChangesJson(
272
+ txIndex: number,
273
+ keySecret: KeySecret,
274
+ ): string {
275
+ const output = this.sessionLog.decryptNextTransactionChangesJson(
276
+ txIndex,
277
+ keySecret,
278
+ );
279
+ return output;
280
+ }
281
+
282
+ free() {
283
+ this.sessionLog.free();
284
+ }
285
+
286
+ clone(): SessionLogAdapter {
287
+ return new SessionLogAdapter(this.sessionLog.clone());
288
+ }
198
289
  }