cojson 0.17.10 → 0.17.12

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 (78) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +20 -0
  3. package/dist/coValueCore/SessionMap.d.ts +3 -2
  4. package/dist/coValueCore/SessionMap.d.ts.map +1 -1
  5. package/dist/coValueCore/SessionMap.js.map +1 -1
  6. package/dist/coValueCore/coValueCore.d.ts +9 -4
  7. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  8. package/dist/coValueCore/coValueCore.js +16 -8
  9. package/dist/coValueCore/coValueCore.js.map +1 -1
  10. package/dist/coValueCore/verifiedState.d.ts +2 -2
  11. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  12. package/dist/coValueCore/verifiedState.js +1 -1
  13. package/dist/coValueCore/verifiedState.js.map +1 -1
  14. package/dist/coValues/group.d.ts.map +1 -1
  15. package/dist/coValues/group.js +6 -2
  16. package/dist/coValues/group.js.map +1 -1
  17. package/dist/crypto/PureJSCrypto.d.ts +2 -2
  18. package/dist/crypto/PureJSCrypto.d.ts.map +1 -1
  19. package/dist/crypto/PureJSCrypto.js +3 -0
  20. package/dist/crypto/PureJSCrypto.js.map +1 -1
  21. package/dist/crypto/WasmCrypto.d.ts +1 -1
  22. package/dist/crypto/WasmCrypto.d.ts.map +1 -1
  23. package/dist/crypto/WasmCrypto.js.map +1 -1
  24. package/dist/crypto/crypto.d.ts +1 -1
  25. package/dist/crypto/crypto.d.ts.map +1 -1
  26. package/dist/localNode.js +1 -1
  27. package/dist/localNode.js.map +1 -1
  28. package/dist/permissions.d.ts +17 -1
  29. package/dist/permissions.d.ts.map +1 -1
  30. package/dist/permissions.js.map +1 -1
  31. package/dist/queue/LocalTransactionsSyncQueue.d.ts +15 -0
  32. package/dist/queue/LocalTransactionsSyncQueue.d.ts.map +1 -1
  33. package/dist/queue/LocalTransactionsSyncQueue.js +25 -0
  34. package/dist/queue/LocalTransactionsSyncQueue.js.map +1 -1
  35. package/dist/sync.d.ts +8 -5
  36. package/dist/sync.d.ts.map +1 -1
  37. package/dist/sync.js +78 -66
  38. package/dist/sync.js.map +1 -1
  39. package/dist/tests/PureJSCrypto.test.js +15 -1
  40. package/dist/tests/PureJSCrypto.test.js.map +1 -1
  41. package/dist/tests/WasmCrypto.test.js +1 -1
  42. package/dist/tests/WasmCrypto.test.js.map +1 -1
  43. package/dist/tests/coValueCore.test.js +2 -2
  44. package/dist/tests/coValueCore.test.js.map +1 -1
  45. package/dist/tests/group.addMember.test.js +6 -11
  46. package/dist/tests/group.addMember.test.js.map +1 -1
  47. package/dist/tests/sync.known.test.d.ts +2 -0
  48. package/dist/tests/sync.known.test.d.ts.map +1 -0
  49. package/dist/tests/sync.known.test.js +78 -0
  50. package/dist/tests/sync.known.test.js.map +1 -0
  51. package/dist/tests/sync.load.test.js +40 -1
  52. package/dist/tests/sync.load.test.js.map +1 -1
  53. package/dist/tests/sync.sharding.test.d.ts +2 -0
  54. package/dist/tests/sync.sharding.test.d.ts.map +1 -0
  55. package/dist/tests/sync.sharding.test.js +51 -0
  56. package/dist/tests/sync.sharding.test.js.map +1 -0
  57. package/dist/tests/sync.test.js +30 -1
  58. package/dist/tests/sync.test.js.map +1 -1
  59. package/package.json +2 -2
  60. package/src/coValueCore/SessionMap.ts +4 -5
  61. package/src/coValueCore/coValueCore.ts +42 -32
  62. package/src/coValueCore/verifiedState.ts +1 -3
  63. package/src/coValues/group.ts +10 -2
  64. package/src/crypto/PureJSCrypto.ts +6 -2
  65. package/src/crypto/WasmCrypto.ts +1 -1
  66. package/src/crypto/crypto.ts +1 -1
  67. package/src/localNode.ts +1 -1
  68. package/src/permissions.ts +17 -1
  69. package/src/queue/LocalTransactionsSyncQueue.ts +32 -1
  70. package/src/sync.ts +103 -80
  71. package/src/tests/PureJSCrypto.test.ts +25 -2
  72. package/src/tests/WasmCrypto.test.ts +0 -2
  73. package/src/tests/coValueCore.test.ts +0 -4
  74. package/src/tests/group.addMember.test.ts +69 -63
  75. package/src/tests/sync.known.test.ts +109 -0
  76. package/src/tests/sync.load.test.ts +52 -0
  77. package/src/tests/sync.sharding.test.ts +76 -0
  78. package/src/tests/sync.test.ts +43 -2
@@ -1,5 +1,5 @@
1
1
  import { UpDownCounter, ValueType, metrics } from "@opentelemetry/api";
2
- import { Result, err } from "neverthrow";
2
+ import { Result, err, ok } from "neverthrow";
3
3
  import type { PeerState } from "../PeerState.js";
4
4
  import type { RawCoValue } from "../coValue.js";
5
5
  import type { ControlledAccountOrAgent } from "../coValues/account.js";
@@ -431,43 +431,46 @@ export class CoValueCore {
431
431
  tryAddTransactions(
432
432
  sessionID: SessionID,
433
433
  newTransactions: Transaction[],
434
- givenExpectedNewHash: Hash | undefined,
435
434
  newSignature: Signature,
436
- notifyMode: "immediate" | "deferred",
437
435
  skipVerify: boolean = false,
438
- givenNewStreamingHash?: StreamingHash,
439
436
  ): Result<true, TryAddTransactionsError> {
440
- return this.node
441
- .resolveAccountAgent(
442
- accountOrAgentIDfromSessionID(sessionID),
443
- "Expected to know signer of transaction",
444
- )
445
- .andThen((agent) => {
446
- if (!this.verified) {
447
- return err({
448
- type: "TriedToAddTransactionsWithoutVerifiedState",
449
- id: this.id,
450
- } satisfies TriedToAddTransactionsWithoutVerifiedStateErrpr);
451
- }
437
+ let result: Result<SignerID | undefined, TryAddTransactionsError>;
452
438
 
453
- const signerID = this.crypto.getAgentSignerID(agent);
439
+ if (skipVerify) {
440
+ result = ok(undefined);
441
+ } else {
442
+ result = this.node
443
+ .resolveAccountAgent(
444
+ accountOrAgentIDfromSessionID(sessionID),
445
+ "Expected to know signer of transaction",
446
+ )
447
+ .andThen((agent) => {
448
+ return ok(this.crypto.getAgentSignerID(agent));
449
+ });
450
+ }
454
451
 
455
- const result = this.verified.tryAddTransactions(
456
- sessionID,
457
- signerID,
458
- newTransactions,
459
- givenExpectedNewHash,
460
- newSignature,
461
- skipVerify,
462
- givenNewStreamingHash,
463
- );
452
+ return result.andThen((signerID) => {
453
+ if (!this.verified) {
454
+ return err({
455
+ type: "TriedToAddTransactionsWithoutVerifiedState",
456
+ id: this.id,
457
+ } satisfies TriedToAddTransactionsWithoutVerifiedStateErrpr);
458
+ }
464
459
 
465
- if (result.isOk()) {
466
- this.updateContentAndNotifyUpdate(notifyMode);
467
- }
460
+ const result = this.verified.tryAddTransactions(
461
+ sessionID,
462
+ signerID,
463
+ newTransactions,
464
+ newSignature,
465
+ skipVerify,
466
+ );
468
467
 
469
- return result;
470
- });
468
+ if (result.isOk()) {
469
+ this.updateContentAndNotifyUpdate("immediate");
470
+ }
471
+
472
+ return result;
473
+ });
471
474
  }
472
475
 
473
476
  deferredUpdates = 0;
@@ -973,7 +976,7 @@ export type InvalidSignatureError = {
973
976
  id: RawCoID;
974
977
  newSignature: Signature;
975
978
  sessionID: SessionID;
976
- signerID: SignerID;
979
+ signerID: SignerID | undefined;
977
980
  };
978
981
 
979
982
  export type TriedToAddTransactionsWithoutVerifiedStateErrpr = {
@@ -981,8 +984,15 @@ export type TriedToAddTransactionsWithoutVerifiedStateErrpr = {
981
984
  id: RawCoID;
982
985
  };
983
986
 
987
+ export type TriedToAddTransactionsWithoutSignerIDError = {
988
+ type: "TriedToAddTransactionsWithoutSignerID";
989
+ id: RawCoID;
990
+ sessionID: SessionID;
991
+ };
992
+
984
993
  export type TryAddTransactionsError =
985
994
  | TriedToAddTransactionsWithoutVerifiedStateErrpr
995
+ | TriedToAddTransactionsWithoutSignerIDError
986
996
  | ResolveAccountAgentError
987
997
  | InvalidHashError
988
998
  | InvalidSignatureError;
@@ -89,12 +89,10 @@ export class VerifiedState {
89
89
 
90
90
  tryAddTransactions(
91
91
  sessionID: SessionID,
92
- signerID: SignerID,
92
+ signerID: SignerID | undefined,
93
93
  newTransactions: Transaction[],
94
- givenExpectedNewHash: Hash | undefined,
95
94
  newSignature: Signature,
96
95
  skipVerify: boolean = false,
97
- givenNewStreamingHash?: StreamingHash,
98
96
  ): Result<true, TryAddTransactionsError> {
99
97
  const result = this.sessions.addTransaction(
100
98
  sessionID,
@@ -370,16 +370,24 @@ export class RawGroup<
370
370
  if (role === "writeOnly" || role === "writeOnlyInvite") {
371
371
  const previousRole = this.get(memberKey);
372
372
 
373
- this.set(memberKey, role, "trusting");
373
+ if (
374
+ previousRole === "admin" &&
375
+ memberKey !== this.core.node.getCurrentAgent().id
376
+ ) {
377
+ throw new Error(
378
+ "Administrators cannot demote other administrators in a group",
379
+ );
380
+ }
374
381
 
375
382
  if (
376
383
  previousRole === "reader" ||
377
384
  previousRole === "writer" ||
378
385
  previousRole === "admin"
379
386
  ) {
380
- this.rotateReadKey();
387
+ this.rotateReadKey(memberKey);
381
388
  }
382
389
 
390
+ this.set(memberKey, role, "trusting");
383
391
  this.internalCreateWriteOnlyKeyForMember(memberKey, agent);
384
392
  } else {
385
393
  const currentReadKey = this.getCurrentReadKey();
@@ -212,7 +212,7 @@ export class PureJSCrypto extends CryptoProvider<Blake3State> {
212
212
  createSessionLog(
213
213
  coID: RawCoID,
214
214
  sessionID: SessionID,
215
- signerID: SignerID,
215
+ signerID?: SignerID,
216
216
  ): SessionLogImpl {
217
217
  return new PureJSSessionLog(coID, sessionID, signerID, this);
218
218
  }
@@ -226,7 +226,7 @@ export class PureJSSessionLog implements SessionLogImpl {
226
226
  constructor(
227
227
  private readonly coID: RawCoID,
228
228
  private readonly sessionID: SessionID,
229
- private readonly signerID: SignerID,
229
+ private readonly signerID: SignerID | undefined,
230
230
  private readonly crypto: PureJSCrypto,
231
231
  ) {
232
232
  this.streamingHash = this.crypto.emptyBlake3State();
@@ -263,6 +263,10 @@ export class PureJSSessionLog implements SessionLogImpl {
263
263
  skipVerify: boolean,
264
264
  ) {
265
265
  if (!skipVerify) {
266
+ if (!this.signerID) {
267
+ throw new Error("Tried to add transactions without signer ID");
268
+ }
269
+
266
270
  const checkHasher = this.crypto.cloneBlake3State(this.streamingHash);
267
271
 
268
272
  for (const tx of transactions) {
@@ -205,7 +205,7 @@ export class WasmCrypto extends CryptoProvider<Blake3State> {
205
205
  }
206
206
  }
207
207
 
208
- createSessionLog(coID: RawCoID, sessionID: SessionID, signerID: SignerID) {
208
+ createSessionLog(coID: RawCoID, sessionID: SessionID, signerID?: SignerID) {
209
209
  return new SessionLogAdapter(new SessionLog(coID, sessionID, signerID));
210
210
  }
211
211
  }
@@ -306,7 +306,7 @@ export abstract class CryptoProvider<Blake3State = any> {
306
306
  abstract createSessionLog(
307
307
  coID: RawCoID,
308
308
  sessionID: SessionID,
309
- signerID: SignerID,
309
+ signerID?: SignerID,
310
310
  ): SessionLogImpl;
311
311
  }
312
312
 
package/src/localNode.ts CHANGED
@@ -408,7 +408,7 @@ export class LocalNode {
408
408
  coValue.loadingState === "unknown" ||
409
409
  coValue.loadingState === "unavailable"
410
410
  ) {
411
- const peers = this.syncManager.getServerPeers(skipLoadingFromPeer);
411
+ const peers = this.syncManager.getServerPeers(id, skipLoadingFromPeer);
412
412
 
413
413
  if (!this.storage && peers.length === 0) {
414
414
  return coValue;
@@ -31,7 +31,23 @@ export type PermissionsDef =
31
31
  | { type: "ownedByGroup"; group: RawCoID }
32
32
  | { type: "unsafeAllowAll" };
33
33
 
34
- export type AccountRole = "reader" | "writer" | "admin" | "writeOnly";
34
+ export type AccountRole =
35
+ /**
36
+ * Can read the group's CoValues
37
+ */
38
+ | "reader"
39
+ /**
40
+ * Can read and write to the group's CoValues
41
+ */
42
+ | "writer"
43
+ /**
44
+ * Can read and write to the group, and change group member roles
45
+ */
46
+ | "admin"
47
+ /**
48
+ * Can only write to the group's CoValues and read their own changes
49
+ */
50
+ | "writeOnly";
35
51
 
36
52
  export type Role =
37
53
  | AccountRole
@@ -4,7 +4,7 @@ import {
4
4
  } from "../coValueContentMessage.js";
5
5
  import { Transaction, VerifiedState } from "../coValueCore/verifiedState.js";
6
6
  import { Signature } from "../crypto/crypto.js";
7
- import { SessionID } from "../ids.js";
7
+ import { RawCoID, SessionID } from "../ids.js";
8
8
  import { NewContentMessage } from "../sync.js";
9
9
  import { LinkedList } from "./LinkedList.js";
10
10
 
@@ -73,8 +73,39 @@ export class LocalTransactionsSyncQueue {
73
73
  this.queue.push(content);
74
74
 
75
75
  this.processPendingSyncs();
76
+
77
+ for (const trackingSet of this.dirtyCoValuesTrackingSets) {
78
+ trackingSet.add(content.id);
79
+ }
76
80
  }
77
81
 
82
+ private dirtyCoValuesTrackingSets: Set<Set<RawCoID>> = new Set();
83
+
84
+ /**
85
+ * It starts tracking all changed CoValues. Returns a `done()` function that returns a set of coValues' ids that have been modified since the start.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * const tracking = node.syncManager.trackDirtyCoValues();
90
+ * // Any CoValue mutation
91
+ * const tracked = tracking.done();
92
+ * console.log("CoValue mutated: " Array.from(tracked))
93
+ * ```
94
+ */
95
+ trackDirtyCoValues = () => {
96
+ const trackingSet = new Set<RawCoID>();
97
+
98
+ this.dirtyCoValuesTrackingSets.add(trackingSet);
99
+
100
+ return {
101
+ done: () => {
102
+ this.dirtyCoValuesTrackingSets.delete(trackingSet);
103
+
104
+ return trackingSet;
105
+ },
106
+ };
107
+ };
108
+
78
109
  private processingSyncs = false;
79
110
  processPendingSyncs() {
80
111
  if (this.processingSyncs) return;
package/src/sync.ts CHANGED
@@ -128,6 +128,11 @@ export function combinedKnownStates(
128
128
  };
129
129
  }
130
130
 
131
+ export type ServerPeerSelector = (
132
+ id: RawCoID,
133
+ serverPeers: PeerState[],
134
+ ) => PeerState[];
135
+
131
136
  export class SyncManager {
132
137
  peers: { [key: PeerID]: PeerState } = {};
133
138
  local: LocalNode;
@@ -144,6 +149,8 @@ export class SyncManager {
144
149
  });
145
150
  private transactionsSizeHistogram: Histogram;
146
151
 
152
+ serverPeerSelector?: ServerPeerSelector;
153
+
147
154
  constructor(local: LocalNode) {
148
155
  this.local = local;
149
156
  this.syncState = new SyncStateManager(this);
@@ -176,10 +183,13 @@ export class SyncManager {
176
183
  return Object.values(this.peers);
177
184
  }
178
185
 
179
- getServerPeers(excludePeerId?: PeerID): PeerState[] {
180
- return this.getPeers().filter(
186
+ getServerPeers(id: RawCoID, excludePeerId?: PeerID): PeerState[] {
187
+ const serverPeers = this.getPeers().filter(
181
188
  (peer) => peer.role === "server" && peer.id !== excludePeerId,
182
189
  );
190
+ return this.serverPeerSelector
191
+ ? this.serverPeerSelector(id, serverPeers)
192
+ : serverPeers;
183
193
  }
184
194
 
185
195
  handleSyncMessage(msg: SyncMessage, peer: PeerState) {
@@ -221,6 +231,23 @@ export class SyncManager {
221
231
  id: RawCoID,
222
232
  peer: PeerState,
223
233
  seen: Set<RawCoID> = new Set(),
234
+ ) {
235
+ this.sendNewContent(id, peer, seen, true);
236
+ }
237
+
238
+ sendNewContentWithoutDependencies(
239
+ id: RawCoID,
240
+ peer: PeerState,
241
+ seen: Set<RawCoID> = new Set(),
242
+ ) {
243
+ this.sendNewContent(id, peer, seen, false);
244
+ }
245
+
246
+ private sendNewContent(
247
+ id: RawCoID,
248
+ peer: PeerState,
249
+ seen: Set<RawCoID> = new Set(),
250
+ includeDependencies: boolean,
224
251
  ) {
225
252
  if (seen.has(id)) {
226
253
  return;
@@ -234,8 +261,10 @@ export class SyncManager {
234
261
  return;
235
262
  }
236
263
 
237
- for (const dependency of coValue.getDependedOnCoValues()) {
238
- this.sendNewContentIncludingDependencies(dependency, peer, seen);
264
+ if (includeDependencies) {
265
+ for (const dependency of coValue.getDependedOnCoValues()) {
266
+ this.sendNewContentIncludingDependencies(dependency, peer, seen);
267
+ }
239
268
  }
240
269
 
241
270
  const newContentPieces = coValue.verified.newContentSince(
@@ -402,7 +431,7 @@ export class SyncManager {
402
431
  return;
403
432
  }
404
433
 
405
- const peers = this.getServerPeers(peer.id);
434
+ const peers = this.getServerPeers(msg.id, peer.id);
406
435
 
407
436
  coValue.load(peers);
408
437
 
@@ -442,7 +471,11 @@ export class SyncManager {
442
471
  }
443
472
 
444
473
  if (coValue.isAvailable()) {
445
- this.sendNewContentIncludingDependencies(msg.id, peer);
474
+ if (peer.role === "server") {
475
+ this.sendNewContentWithoutDependencies(msg.id, peer);
476
+ } else {
477
+ this.sendNewContentIncludingDependencies(msg.id, peer);
478
+ }
446
479
  }
447
480
  }
448
481
 
@@ -511,28 +544,31 @@ export class SyncManager {
511
544
  (content) => content.newTransactions,
512
545
  );
513
546
 
514
- for (const dependency of getDependedOnCoValuesFromRawData(
515
- msg.id,
516
- msg.header,
517
- sessionIDs,
518
- transactions,
519
- )) {
520
- const dependencyCoValue = this.local.getCoValue(dependency);
547
+ // If we'll be performing transaction verification, ensure all the dependencies available.
548
+ if (!this.skipVerify) {
549
+ for (const dependency of getDependedOnCoValuesFromRawData(
550
+ msg.id,
551
+ msg.header,
552
+ sessionIDs,
553
+ transactions,
554
+ )) {
555
+ const dependencyCoValue = this.local.getCoValue(dependency);
521
556
 
522
- if (!dependencyCoValue.hasVerifiedContent()) {
523
- coValue.markMissingDependency(dependency);
557
+ if (!dependencyCoValue.hasVerifiedContent()) {
558
+ coValue.markMissingDependency(dependency);
524
559
 
525
- const peers = this.getServerPeers();
560
+ const peers = this.getServerPeers(dependencyCoValue.id);
526
561
 
527
- // if the peer that sent the content is a client, we add it to the list of peers
528
- // to also ask them for the dependency
529
- if (peer?.role === "client") {
530
- peers.push(peer);
531
- }
562
+ // if the peer that sent the content is a client, we add it to the list of peers
563
+ // to also ask them for the dependency
564
+ if (peer?.role === "client") {
565
+ peers.push(peer);
566
+ }
532
567
 
533
- dependencyCoValue.load(peers);
534
- } else if (!dependencyCoValue.isAvailable()) {
535
- coValue.markMissingDependency(dependency);
568
+ dependencyCoValue.load(peers);
569
+ } else if (!dependencyCoValue.isAvailable()) {
570
+ coValue.markMissingDependency(dependency);
571
+ }
536
572
  }
537
573
  }
538
574
 
@@ -592,60 +628,61 @@ export class SyncManager {
592
628
  continue;
593
629
  }
594
630
 
595
- const accountId = accountOrAgentIDfromSessionID(sessionID);
631
+ // If we'll be performing transaction verification, ensure the account is available.
632
+ if (!this.skipVerify) {
633
+ const accountId = accountOrAgentIDfromSessionID(sessionID);
596
634
 
597
- if (isAccountID(accountId)) {
598
- const account = this.local.getCoValue(accountId);
635
+ if (isAccountID(accountId)) {
636
+ const account = this.local.getCoValue(accountId);
599
637
 
600
- // We can't verify the transaction without the account, so we delay the session content handling until the account is available
601
- if (!account.isAvailable()) {
602
- // This covers the case where we are getting a new session on an already loaded coValue
603
- // where we need to load the account to get their public key
604
- if (!coValue.missingDependencies.has(accountId)) {
605
- const peers = this.getServerPeers();
638
+ // We can't verify the transaction without the account, so we delay the session content handling until the account is available
639
+ if (!account.isAvailable()) {
640
+ // This covers the case where we are getting a new session on an already loaded coValue
641
+ // where we need to load the account to get their public key
642
+ if (!coValue.missingDependencies.has(accountId)) {
643
+ const peers = this.getServerPeers(account.id);
606
644
 
607
- if (peer?.role === "client") {
608
- // if the peer that sent the content is a client, we add it to the list of peers
609
- // to also ask them for the dependency
610
- peers.push(peer);
611
- }
645
+ if (peer?.role === "client") {
646
+ // if the peer that sent the content is a client, we add it to the list of peers
647
+ // to also ask them for the dependency
648
+ peers.push(peer);
649
+ }
612
650
 
613
- account.load(peers);
614
- }
651
+ account.load(peers);
652
+ }
615
653
 
616
- // We need to wait for the account to be available before we can verify the transaction
617
- // Currently doing this by delaying the handleNewContent for the session to when we have the account
618
- //
619
- // This is not the best solution, because the knownState is not updated and the ACK response will be given
620
- // by excluding the session.
621
- // This is good enough implementation for now because the only case for the account to be missing are out-of-order
622
- // dependencies push, so the gap should be short lived.
623
- //
624
- // When we are going to have sharded-peers we should revisit this, and store unverified sessions that are considered as part of the
625
- // knwonState, but not actively used until they can be verified.
626
- void account.waitForAvailable().then(() => {
627
- this.handleNewContent(
628
- {
629
- action: "content",
630
- id: coValue.id,
631
- new: {
632
- [sessionID]: newContentForSession,
654
+ // We need to wait for the account to be available before we can verify the transaction
655
+ // Currently doing this by delaying the handleNewContent for the session to when we have the account
656
+ //
657
+ // This is not the best solution, because the knownState is not updated and the ACK response will be given
658
+ // by excluding the session.
659
+ // This is good enough implementation for now because the only case for the account to be missing are out-of-order
660
+ // dependencies push, so the gap should be short lived.
661
+ //
662
+ // When we are going to have sharded-peers we should revisit this, and store unverified sessions that are considered as part of the
663
+ // knwonState, but not actively used until they can be verified.
664
+ void account.waitForAvailable().then(() => {
665
+ this.handleNewContent(
666
+ {
667
+ action: "content",
668
+ id: coValue.id,
669
+ new: {
670
+ [sessionID]: newContentForSession,
671
+ },
672
+ priority: msg.priority,
633
673
  },
634
- priority: msg.priority,
635
- },
636
- from,
637
- );
638
- });
639
- continue;
674
+ from,
675
+ );
676
+ });
677
+ continue;
678
+ }
640
679
  }
641
680
  }
642
681
 
643
682
  const result = coValue.tryAddTransactions(
644
683
  sessionID,
645
684
  newTransactions,
646
- undefined,
647
685
  newContentForSession.lastSignature,
648
- "immediate",
649
686
  this.skipVerify,
650
687
  );
651
688
 
@@ -767,26 +804,12 @@ export class SyncManager {
767
804
  return this.sendNewContentIncludingDependencies(msg.id, peer);
768
805
  }
769
806
 
770
- dirtyCoValuesTrackingSets: Set<Set<RawCoID>> = new Set();
771
- trackDirtyCoValues() {
772
- const trackingSet = new Set<RawCoID>();
773
-
774
- this.dirtyCoValuesTrackingSets.add(trackingSet);
775
-
776
- return {
777
- done: () => {
778
- this.dirtyCoValuesTrackingSets.delete(trackingSet);
779
-
780
- return trackingSet;
781
- },
782
- };
783
- }
784
-
785
807
  private syncQueue = new LocalTransactionsSyncQueue((content) =>
786
808
  this.syncContent(content),
787
809
  );
788
810
  syncHeader = this.syncQueue.syncHeader;
789
811
  syncLocalTransaction = this.syncQueue.syncTransaction;
812
+ trackDirtyCoValues = this.syncQueue.trackDirtyCoValues;
790
813
 
791
814
  syncContent(content: NewContentMessage) {
792
815
  const coValue = this.local.getCoValue(content.id);
@@ -4,6 +4,7 @@ import {
4
4
  setCurrentTestCryptoProvider,
5
5
  setupTestNode,
6
6
  setupTestAccount,
7
+ randomAgentAndSessionID,
7
8
  } from "./testUtils";
8
9
  import { PureJSCrypto } from "../crypto/PureJSCrypto";
9
10
  import { stableStringify } from "../jsonStringify";
@@ -113,9 +114,7 @@ describe("PureJSCrypto", () => {
113
114
  madeAt: Date.now(),
114
115
  },
115
116
  ],
116
- "hash_z12345678",
117
117
  "signature_z12345678",
118
- "immediate",
119
118
  true,
120
119
  );
121
120
 
@@ -128,3 +127,27 @@ describe("PureJSCrypto", () => {
128
127
  expect(map.get("count")).toEqual(0);
129
128
  });
130
129
  });
130
+
131
+ describe("PureJSSessionLog", () => {
132
+ it("fails to verify signatures without a signer ID", async () => {
133
+ const agentSecret = jsCrypto.newRandomAgentSecret();
134
+ const sessionID = jsCrypto.newRandomSessionID(
135
+ jsCrypto.getAgentID(agentSecret),
136
+ );
137
+
138
+ const sessionLog = jsCrypto.createSessionLog("co_z12345678", sessionID);
139
+ expect(() =>
140
+ sessionLog.tryAdd(
141
+ [
142
+ {
143
+ privacy: "trusting",
144
+ changes: stableStringify([{ op: "set", key: "count", value: 1 }]),
145
+ madeAt: Date.now(),
146
+ },
147
+ ],
148
+ "signature_z12345678",
149
+ false,
150
+ ),
151
+ ).toThrow("Tried to add transactions without signer ID");
152
+ });
153
+ });
@@ -113,9 +113,7 @@ describe("WasmCrypto", () => {
113
113
  madeAt: Date.now(),
114
114
  },
115
115
  ],
116
- "hash_z12345678",
117
116
  "signature_z12345678",
118
- "immediate",
119
117
  true,
120
118
  );
121
119
 
@@ -76,9 +76,7 @@ test("transactions with wrong signature are rejected", () => {
76
76
  const result = newEntry.tryAddTransactions(
77
77
  node.currentSessionID,
78
78
  [transaction],
79
- undefined,
80
79
  signature,
81
- "immediate",
82
80
  );
83
81
 
84
82
  expect(result.isErr()).toBe(true);
@@ -301,9 +299,7 @@ test("getValidTransactions should skip private transactions with invalid JSON",
301
299
  .tryAddTransactions(
302
300
  fixtures.session,
303
301
  [fixtures.transaction],
304
- undefined,
305
302
  fixtures.signature,
306
- "immediate",
307
303
  )
308
304
  ._unsafeUnwrap();
309
305