cojson 0.18.34 → 0.18.36
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +16 -0
- package/dist/coValueContentMessage.d.ts +5 -2
- package/dist/coValueContentMessage.d.ts.map +1 -1
- package/dist/coValueContentMessage.js +15 -0
- package/dist/coValueContentMessage.js.map +1 -1
- package/dist/coValueCore/SessionMap.d.ts +4 -3
- package/dist/coValueCore/SessionMap.d.ts.map +1 -1
- package/dist/coValueCore/SessionMap.js +12 -15
- package/dist/coValueCore/SessionMap.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +14 -6
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +29 -48
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/verifiedState.d.ts +3 -3
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValueCore/verifiedState.js +18 -10
- package/dist/coValueCore/verifiedState.js.map +1 -1
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +21 -16
- package/dist/coValues/group.js.map +1 -1
- package/dist/knownState.d.ts.map +1 -1
- package/dist/knownState.js.map +1 -1
- package/dist/localNode.d.ts +7 -2
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +8 -14
- package/dist/localNode.js.map +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +15 -18
- package/dist/sync.js.map +1 -1
- package/dist/tests/coValueContentMessage.test.js +130 -1
- package/dist/tests/coValueContentMessage.test.js.map +1 -1
- package/dist/tests/coValueCore.test.js +3 -6
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/group.childKeyRotation.test.js +6 -6
- package/dist/tests/group.childKeyRotation.test.js.map +1 -1
- package/package.json +3 -4
- package/src/coValueContentMessage.ts +29 -2
- package/src/coValueCore/SessionMap.ts +23 -16
- package/src/coValueCore/coValueCore.ts +37 -63
- package/src/coValueCore/verifiedState.ts +25 -15
- package/src/coValues/group.ts +41 -28
- package/src/knownState.ts +3 -12
- package/src/localNode.ts +10 -18
- package/src/sync.ts +24 -25
- package/src/tests/coValueContentMessage.test.ts +197 -2
- package/src/tests/coValueCore.test.ts +7 -10
- package/src/tests/group.childKeyRotation.test.ts +6 -6
|
@@ -3,9 +3,9 @@ import { TRANSACTION_CONFIG } from "./config.js";
|
|
|
3
3
|
import { Signature } from "./crypto/crypto.js";
|
|
4
4
|
import { RawCoID, SessionID } from "./ids.js";
|
|
5
5
|
import { JsonValue } from "./jsonValue.js";
|
|
6
|
-
import { emptyKnownState } from "./knownState.js";
|
|
6
|
+
import { CoValueKnownState, emptyKnownState } from "./knownState.js";
|
|
7
7
|
import { getPriorityFromHeader } from "./priority.js";
|
|
8
|
-
import { NewContentMessage } from "./sync.js";
|
|
8
|
+
import { NewContentMessage, SessionNewContent } from "./sync.js";
|
|
9
9
|
|
|
10
10
|
export function createContentMessage(
|
|
11
11
|
id: RawCoID,
|
|
@@ -104,3 +104,30 @@ export function getContenDebugInfo(msg: NewContentMessage) {
|
|
|
104
104
|
`Session: ${sessionID} After: ${sessionNewContent.after} New: ${sessionNewContent.newTransactions.length}`,
|
|
105
105
|
);
|
|
106
106
|
}
|
|
107
|
+
|
|
108
|
+
export function getNewTransactionsFromContentMessage(
|
|
109
|
+
content: SessionNewContent,
|
|
110
|
+
knownState: CoValueKnownState,
|
|
111
|
+
sessionID: SessionID,
|
|
112
|
+
) {
|
|
113
|
+
const ourKnownTxIdx = knownState.sessions[sessionID] ?? 0;
|
|
114
|
+
const theirFirstNewTxIdx = content.after;
|
|
115
|
+
|
|
116
|
+
if (ourKnownTxIdx < theirFirstNewTxIdx) {
|
|
117
|
+
// Flagging invalid state assumption by returning undefined
|
|
118
|
+
// not throwing an error or return an error object for performance reasons
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const alreadyKnownOffset = ourKnownTxIdx - theirFirstNewTxIdx;
|
|
123
|
+
|
|
124
|
+
const newTransactions = content.newTransactions.slice(alreadyKnownOffset);
|
|
125
|
+
|
|
126
|
+
return newTransactions;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function getSessionEntriesFromContentMessage(
|
|
130
|
+
content: NewContentMessage,
|
|
131
|
+
) {
|
|
132
|
+
return Object.entries(content.new) as [SessionID, SessionNewContent][];
|
|
133
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Result, err, ok } from "neverthrow";
|
|
2
1
|
import { ControlledAccountOrAgent } from "../coValues/account.js";
|
|
3
2
|
import type {
|
|
4
3
|
CryptoProvider,
|
|
@@ -40,6 +39,9 @@ export class SessionMap {
|
|
|
40
39
|
// Known state related properies, mutated when adding transactions to the session map
|
|
41
40
|
knownState: CoValueKnownState;
|
|
42
41
|
knownStateWithStreaming: CoValueKnownState | undefined;
|
|
42
|
+
// The immutable version of the known statuses, to get a different reference when the known state is updated
|
|
43
|
+
immutableKnownState: CoValueKnownState;
|
|
44
|
+
immutableKnownStateWithStreaming: CoValueKnownState | undefined;
|
|
43
45
|
streamingKnownState?: KnownStateSessions;
|
|
44
46
|
|
|
45
47
|
constructor(
|
|
@@ -48,6 +50,7 @@ export class SessionMap {
|
|
|
48
50
|
streamingKnownState?: KnownStateSessions,
|
|
49
51
|
) {
|
|
50
52
|
this.knownState = { id: this.id, header: true, sessions: {} };
|
|
53
|
+
this.immutableKnownState = { id: this.id, header: true, sessions: {} };
|
|
51
54
|
if (streamingKnownState) {
|
|
52
55
|
this.streamingKnownState = { ...streamingKnownState };
|
|
53
56
|
this.knownStateWithStreaming = {
|
|
@@ -55,6 +58,9 @@ export class SessionMap {
|
|
|
55
58
|
header: true,
|
|
56
59
|
sessions: { ...streamingKnownState },
|
|
57
60
|
};
|
|
61
|
+
this.immutableKnownStateWithStreaming = cloneKnownState(
|
|
62
|
+
this.knownStateWithStreaming,
|
|
63
|
+
);
|
|
58
64
|
}
|
|
59
65
|
}
|
|
60
66
|
|
|
@@ -86,6 +92,17 @@ export class SessionMap {
|
|
|
86
92
|
this.knownStateWithStreaming.sessions,
|
|
87
93
|
actualStreamingKnownState,
|
|
88
94
|
);
|
|
95
|
+
|
|
96
|
+
this.immutableKnownStateWithStreaming = cloneKnownState(
|
|
97
|
+
this.knownStateWithStreaming,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
updateImmutableKnownState() {
|
|
102
|
+
this.immutableKnownState = cloneKnownState(this.knownState);
|
|
103
|
+
this.immutableKnownStateWithStreaming = this.knownStateWithStreaming
|
|
104
|
+
? cloneKnownState(this.knownStateWithStreaming)
|
|
105
|
+
: undefined;
|
|
89
106
|
}
|
|
90
107
|
|
|
91
108
|
get(sessionID: SessionID): SessionLog | undefined {
|
|
@@ -120,24 +137,12 @@ export class SessionMap {
|
|
|
120
137
|
newTransactions: Transaction[],
|
|
121
138
|
newSignature: Signature,
|
|
122
139
|
skipVerify: boolean = false,
|
|
123
|
-
)
|
|
140
|
+
) {
|
|
124
141
|
const sessionLog = this.getOrCreateSessionLog(sessionID, signerID);
|
|
125
142
|
|
|
126
|
-
|
|
127
|
-
sessionLog.impl.tryAdd(newTransactions, newSignature, skipVerify);
|
|
128
|
-
|
|
129
|
-
this.addTransactionsToJsLog(sessionLog, newTransactions, newSignature);
|
|
143
|
+
sessionLog.impl.tryAdd(newTransactions, newSignature, skipVerify);
|
|
130
144
|
|
|
131
|
-
|
|
132
|
-
} catch (e) {
|
|
133
|
-
return err({
|
|
134
|
-
type: "InvalidSignature",
|
|
135
|
-
id: this.id,
|
|
136
|
-
sessionID,
|
|
137
|
-
newSignature,
|
|
138
|
-
signerID,
|
|
139
|
-
} satisfies TryAddTransactionsError);
|
|
140
|
-
}
|
|
145
|
+
this.addTransactionsToJsLog(sessionLog, newTransactions, newSignature);
|
|
141
146
|
}
|
|
142
147
|
|
|
143
148
|
makeNewPrivateTransaction(
|
|
@@ -250,6 +255,8 @@ export class SessionMap {
|
|
|
250
255
|
transactionsCount,
|
|
251
256
|
);
|
|
252
257
|
}
|
|
258
|
+
|
|
259
|
+
this.updateImmutableKnownState();
|
|
253
260
|
}
|
|
254
261
|
|
|
255
262
|
decryptTransaction(
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { UpDownCounter, ValueType, metrics } from "@opentelemetry/api";
|
|
2
|
-
import { Result, err, ok } from "neverthrow";
|
|
3
2
|
import type { PeerState } from "../PeerState.js";
|
|
4
3
|
import type { RawCoValue } from "../coValue.js";
|
|
5
4
|
import type { ControlledAccountOrAgent } from "../coValues/account.js";
|
|
@@ -201,7 +200,7 @@ export class CoValueCore {
|
|
|
201
200
|
}
|
|
202
201
|
| {
|
|
203
202
|
type: "errored";
|
|
204
|
-
error:
|
|
203
|
+
error: unknown;
|
|
205
204
|
}
|
|
206
205
|
>();
|
|
207
206
|
|
|
@@ -474,12 +473,10 @@ export class CoValueCore {
|
|
|
474
473
|
new SessionMap(this.id, this.node.crypto, streamingKnownState),
|
|
475
474
|
);
|
|
476
475
|
|
|
477
|
-
this.resetKnownStateCache();
|
|
478
|
-
|
|
479
476
|
return true;
|
|
480
477
|
}
|
|
481
478
|
|
|
482
|
-
markErrored(peerId: PeerID, error:
|
|
479
|
+
markErrored(peerId: PeerID, error: unknown) {
|
|
483
480
|
const previousState = this.loadingState;
|
|
484
481
|
this.loadingStatuses.set(peerId, { type: "errored", error });
|
|
485
482
|
this.updateCounter(previousState);
|
|
@@ -531,27 +528,18 @@ export class CoValueCore {
|
|
|
531
528
|
.then((core) => core.getCurrentContent());
|
|
532
529
|
}
|
|
533
530
|
|
|
534
|
-
private _cachedKnownStateWithStreaming?: CoValueKnownState;
|
|
535
531
|
/**
|
|
536
532
|
* Returns the known state considering the known state of the streaming source
|
|
537
533
|
*
|
|
538
534
|
* Used to correctly manage the content & subscriptions during the content streaming process
|
|
539
535
|
*/
|
|
540
536
|
knownStateWithStreaming(): CoValueKnownState {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
const knownState = this.verified
|
|
546
|
-
? cloneKnownState(this.verified.knownStateWithStreaming())
|
|
547
|
-
: emptyKnownState(this.id);
|
|
548
|
-
|
|
549
|
-
this._cachedKnownStateWithStreaming = knownState;
|
|
550
|
-
|
|
551
|
-
return knownState;
|
|
537
|
+
return (
|
|
538
|
+
this.verified?.immutableKnownStateWithStreaming() ??
|
|
539
|
+
emptyKnownState(this.id)
|
|
540
|
+
);
|
|
552
541
|
}
|
|
553
542
|
|
|
554
|
-
private _cachedKnownState?: CoValueKnownState;
|
|
555
543
|
/**
|
|
556
544
|
* Returns the known state of the CoValue
|
|
557
545
|
*
|
|
@@ -560,17 +548,7 @@ export class CoValueCore {
|
|
|
560
548
|
* On change the knownState is invalidated and a new object is returned.
|
|
561
549
|
*/
|
|
562
550
|
knownState(): CoValueKnownState {
|
|
563
|
-
|
|
564
|
-
return this._cachedKnownState;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
const knownState = this.verified
|
|
568
|
-
? cloneKnownState(this.verified.knownState())
|
|
569
|
-
: emptyKnownState(this.id);
|
|
570
|
-
|
|
571
|
-
this._cachedKnownState = knownState;
|
|
572
|
-
|
|
573
|
-
return knownState;
|
|
551
|
+
return this.verified?.immutableKnownState() ?? emptyKnownState(this.id);
|
|
574
552
|
}
|
|
575
553
|
|
|
576
554
|
get meta(): JsonValue {
|
|
@@ -612,31 +590,36 @@ export class CoValueCore {
|
|
|
612
590
|
newTransactions: Transaction[],
|
|
613
591
|
newSignature: Signature,
|
|
614
592
|
skipVerify: boolean = false,
|
|
615
|
-
)
|
|
616
|
-
let
|
|
593
|
+
) {
|
|
594
|
+
let signerID: SignerID | undefined;
|
|
617
595
|
|
|
618
|
-
if (skipVerify) {
|
|
619
|
-
result =
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
accountOrAgentIDfromSessionID(sessionID),
|
|
624
|
-
"Expected to know signer of transaction",
|
|
625
|
-
)
|
|
626
|
-
.andThen((agent) => {
|
|
627
|
-
return ok(this.crypto.getAgentSignerID(agent));
|
|
628
|
-
});
|
|
629
|
-
}
|
|
596
|
+
if (!skipVerify) {
|
|
597
|
+
const result = this.node.resolveAccountAgent(
|
|
598
|
+
accountOrAgentIDfromSessionID(sessionID),
|
|
599
|
+
"Expected to know signer of transaction",
|
|
600
|
+
);
|
|
630
601
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
type: "TriedToAddTransactionsWithoutVerifiedState",
|
|
602
|
+
if (result.error || !result.value) {
|
|
603
|
+
return {
|
|
604
|
+
type: "ResolveAccountAgentError",
|
|
635
605
|
id: this.id,
|
|
636
|
-
|
|
606
|
+
error: result.error,
|
|
607
|
+
} as const;
|
|
637
608
|
}
|
|
638
609
|
|
|
639
|
-
|
|
610
|
+
signerID = this.crypto.getAgentSignerID(result.value);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (!this.verified) {
|
|
614
|
+
return {
|
|
615
|
+
type: "TriedToAddTransactionsWithoutVerifiedState",
|
|
616
|
+
id: this.id,
|
|
617
|
+
error: undefined,
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
try {
|
|
622
|
+
this.verified.tryAddTransactions(
|
|
640
623
|
sessionID,
|
|
641
624
|
signerID,
|
|
642
625
|
newTransactions,
|
|
@@ -644,19 +627,11 @@ export class CoValueCore {
|
|
|
644
627
|
skipVerify,
|
|
645
628
|
);
|
|
646
629
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
return result;
|
|
654
|
-
});
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
private resetKnownStateCache() {
|
|
658
|
-
this._cachedKnownState = undefined;
|
|
659
|
-
this._cachedKnownStateWithStreaming = undefined;
|
|
630
|
+
this.processNewTransactions();
|
|
631
|
+
this.scheduleNotifyUpdate();
|
|
632
|
+
} catch (e) {
|
|
633
|
+
return { type: "InvalidSignature", id: this.id, error: e } as const;
|
|
634
|
+
}
|
|
660
635
|
}
|
|
661
636
|
|
|
662
637
|
private processNewTransactions() {
|
|
@@ -805,7 +780,6 @@ export class CoValueCore {
|
|
|
805
780
|
|
|
806
781
|
this.node.syncManager.recordTransactionsSize([transaction], "local");
|
|
807
782
|
|
|
808
|
-
this.resetKnownStateCache();
|
|
809
783
|
this.processNewTransactions();
|
|
810
784
|
this.addDependenciesFromNewTransaction(transaction);
|
|
811
785
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Result, err, ok } from "neverthrow";
|
|
2
1
|
import { AnyRawCoValue } from "../coValue.js";
|
|
3
2
|
import {
|
|
4
3
|
createContentMessage,
|
|
@@ -94,16 +93,14 @@ export class VerifiedState {
|
|
|
94
93
|
newTransactions: Transaction[],
|
|
95
94
|
newSignature: Signature,
|
|
96
95
|
skipVerify: boolean = false,
|
|
97
|
-
)
|
|
98
|
-
|
|
96
|
+
) {
|
|
97
|
+
this.sessions.addTransaction(
|
|
99
98
|
sessionID,
|
|
100
99
|
signerID,
|
|
101
100
|
newTransactions,
|
|
102
101
|
newSignature,
|
|
103
102
|
skipVerify,
|
|
104
103
|
);
|
|
105
|
-
|
|
106
|
-
return result;
|
|
107
104
|
}
|
|
108
105
|
|
|
109
106
|
makeNewTrustingTransaction(
|
|
@@ -293,16 +290,18 @@ export class VerifiedState {
|
|
|
293
290
|
);
|
|
294
291
|
|
|
295
292
|
if (piecesWithContent.length > 1 || this.isStreaming()) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
293
|
+
if (!opts?.skipExpectContentUntil) {
|
|
294
|
+
// Flag that more content is coming
|
|
295
|
+
if (knownState) {
|
|
296
|
+
firstPiece.expectContentUntil = getKnownStateToSend(
|
|
297
|
+
this.knownStateWithStreaming().sessions,
|
|
298
|
+
knownState.sessions,
|
|
299
|
+
);
|
|
300
|
+
} else {
|
|
301
|
+
firstPiece.expectContentUntil = {
|
|
302
|
+
...this.knownStateWithStreaming().sessions,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
306
305
|
}
|
|
307
306
|
}
|
|
308
307
|
|
|
@@ -321,6 +320,17 @@ export class VerifiedState {
|
|
|
321
320
|
return this.sessions.knownStateWithStreaming ?? this.knownState();
|
|
322
321
|
}
|
|
323
322
|
|
|
323
|
+
immutableKnownState() {
|
|
324
|
+
return this.sessions.immutableKnownState;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
immutableKnownStateWithStreaming() {
|
|
328
|
+
return (
|
|
329
|
+
this.sessions.immutableKnownStateWithStreaming ??
|
|
330
|
+
this.immutableKnownState()
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
324
334
|
isStreaming(): boolean {
|
|
325
335
|
return Boolean(this.sessions.knownStateWithStreaming);
|
|
326
336
|
}
|
package/src/coValues/group.ts
CHANGED
|
@@ -206,7 +206,7 @@ export class RawGroup<
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
const runMigrations = () => {
|
|
209
|
-
rotateReadKeyIfNeeded(this);
|
|
209
|
+
// rotateReadKeyIfNeeded(this);
|
|
210
210
|
healMissingKeyForEveryone(this);
|
|
211
211
|
};
|
|
212
212
|
|
|
@@ -508,12 +508,14 @@ export class RawGroup<
|
|
|
508
508
|
memberRole === "writerInvite" ||
|
|
509
509
|
memberRole === "adminInvite"
|
|
510
510
|
) {
|
|
511
|
-
const otherMemberAgent = this.core.node
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
511
|
+
const otherMemberAgent = this.core.node.resolveAccountAgent(
|
|
512
|
+
otherMemberKey,
|
|
513
|
+
"Expected member agent to be loaded",
|
|
514
|
+
).value;
|
|
515
|
+
|
|
516
|
+
if (!otherMemberAgent) {
|
|
517
|
+
throw new Error("Expected member agent to be loaded");
|
|
518
|
+
}
|
|
517
519
|
|
|
518
520
|
this.storeKeyRevelationForMember(
|
|
519
521
|
otherMemberKey,
|
|
@@ -689,9 +691,14 @@ export class RawGroup<
|
|
|
689
691
|
|
|
690
692
|
if (lastReadyKeyEdit?.value) {
|
|
691
693
|
const revealer = lastReadyKeyEdit.by;
|
|
692
|
-
const revealerAgent = core.node
|
|
693
|
-
|
|
694
|
-
|
|
694
|
+
const revealerAgent = core.node.resolveAccountAgent(
|
|
695
|
+
revealer,
|
|
696
|
+
"Expected to know revealer",
|
|
697
|
+
).value;
|
|
698
|
+
|
|
699
|
+
if (!revealerAgent) {
|
|
700
|
+
throw new Error("Expected to know revealer");
|
|
701
|
+
}
|
|
695
702
|
|
|
696
703
|
const secret = this.crypto.unseal(
|
|
697
704
|
lastReadyKeyEdit.value,
|
|
@@ -841,12 +848,14 @@ export class RawGroup<
|
|
|
841
848
|
const newReadKey = this.crypto.newRandomKeySecret();
|
|
842
849
|
|
|
843
850
|
for (const readerID of currentlyPermittedReaders) {
|
|
844
|
-
const agent = this.core.node
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
851
|
+
const agent = this.core.node.resolveAccountAgent(
|
|
852
|
+
readerID,
|
|
853
|
+
"Expected to know currently permitted reader",
|
|
854
|
+
).value;
|
|
855
|
+
|
|
856
|
+
if (!agent) {
|
|
857
|
+
throw new Error("Expected to know currently permitted reader");
|
|
858
|
+
}
|
|
850
859
|
|
|
851
860
|
this.storeKeyRevelationForMember(
|
|
852
861
|
readerID,
|
|
@@ -861,12 +870,14 @@ export class RawGroup<
|
|
|
861
870
|
* and reveal them to the other non-writeOnly members
|
|
862
871
|
*/
|
|
863
872
|
for (const writeOnlyMemberID of writeOnlyMembers) {
|
|
864
|
-
const agent = this.core.node
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
873
|
+
const agent = this.core.node.resolveAccountAgent(
|
|
874
|
+
writeOnlyMemberID,
|
|
875
|
+
"Expected to know writeOnly member",
|
|
876
|
+
).value;
|
|
877
|
+
|
|
878
|
+
if (!agent) {
|
|
879
|
+
throw new Error("Expected to know writeOnly member");
|
|
880
|
+
}
|
|
870
881
|
|
|
871
882
|
const writeOnlyKey = this.crypto.newRandomKeySecret();
|
|
872
883
|
|
|
@@ -879,12 +890,14 @@ export class RawGroup<
|
|
|
879
890
|
this.set(`writeKeyFor_${writeOnlyMemberID}`, writeOnlyKey.id, "trusting");
|
|
880
891
|
|
|
881
892
|
for (const readerID of currentlyPermittedReaders) {
|
|
882
|
-
const agent = this.core.node
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
893
|
+
const agent = this.core.node.resolveAccountAgent(
|
|
894
|
+
readerID,
|
|
895
|
+
"Expected to know currently permitted reader",
|
|
896
|
+
).value;
|
|
897
|
+
|
|
898
|
+
if (!agent) {
|
|
899
|
+
throw new Error("Expected to know currently permitted reader");
|
|
900
|
+
}
|
|
888
901
|
|
|
889
902
|
this.storeKeyRevelationForMember(
|
|
890
903
|
readerID,
|
package/src/knownState.ts
CHANGED
|
@@ -113,10 +113,7 @@ export function areCurrentSessionsInSyncWith(
|
|
|
113
113
|
current: Record<string, number>,
|
|
114
114
|
target: Record<string, number>,
|
|
115
115
|
) {
|
|
116
|
-
for (const [sessionId, currentCount] of Object.entries(current)
|
|
117
|
-
SessionID,
|
|
118
|
-
number,
|
|
119
|
-
][]) {
|
|
116
|
+
for (const [sessionId, currentCount] of Object.entries(current)) {
|
|
120
117
|
const targetCount = target[sessionId] ?? 0;
|
|
121
118
|
if (currentCount !== targetCount) {
|
|
122
119
|
return false;
|
|
@@ -133,10 +130,7 @@ export function isKnownStateSubsetOf(
|
|
|
133
130
|
current: Record<string, number>,
|
|
134
131
|
target: Record<string, number>,
|
|
135
132
|
) {
|
|
136
|
-
for (const [sessionId, currentCount] of Object.entries(current)
|
|
137
|
-
SessionID,
|
|
138
|
-
number,
|
|
139
|
-
][]) {
|
|
133
|
+
for (const [sessionId, currentCount] of Object.entries(current)) {
|
|
140
134
|
const targetCount = target[sessionId] ?? 0;
|
|
141
135
|
if (currentCount > targetCount) {
|
|
142
136
|
return false;
|
|
@@ -154,10 +148,7 @@ export function getKnownStateToSend(
|
|
|
154
148
|
target: Record<string, number>,
|
|
155
149
|
) {
|
|
156
150
|
const toSend: Record<string, number> = {};
|
|
157
|
-
for (const [sessionId, currentCount] of Object.entries(current)
|
|
158
|
-
SessionID,
|
|
159
|
-
number,
|
|
160
|
-
][]) {
|
|
151
|
+
for (const [sessionId, currentCount] of Object.entries(current)) {
|
|
161
152
|
const targetCount = target[sessionId] ?? 0;
|
|
162
153
|
if (currentCount > targetCount) {
|
|
163
154
|
toSend[sessionId] = currentCount;
|
package/src/localNode.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Result, err, ok } from "neverthrow";
|
|
2
1
|
import { GarbageCollector } from "./GarbageCollector.js";
|
|
3
2
|
import type { CoID } from "./coValue.js";
|
|
4
3
|
import type { RawCoValue } from "./coValue.js";
|
|
@@ -689,12 +688,9 @@ export class LocalNode {
|
|
|
689
688
|
}
|
|
690
689
|
|
|
691
690
|
/** @internal */
|
|
692
|
-
resolveAccountAgent(
|
|
693
|
-
id: RawAccountID | AgentID,
|
|
694
|
-
expectation?: string,
|
|
695
|
-
): Result<AgentID, ResolveAccountAgentError> {
|
|
691
|
+
resolveAccountAgent(id: RawAccountID | AgentID, expectation?: string) {
|
|
696
692
|
if (isAgentID(id)) {
|
|
697
|
-
return
|
|
693
|
+
return { value: id, error: undefined };
|
|
698
694
|
}
|
|
699
695
|
|
|
700
696
|
let coValue: AvailableCoValueCore;
|
|
@@ -702,12 +698,7 @@ export class LocalNode {
|
|
|
702
698
|
try {
|
|
703
699
|
coValue = this.expectCoValueLoaded(id, expectation);
|
|
704
700
|
} catch (e) {
|
|
705
|
-
return
|
|
706
|
-
type: "ErrorLoadingCoValueCore",
|
|
707
|
-
expectation,
|
|
708
|
-
id,
|
|
709
|
-
error: e,
|
|
710
|
-
} satisfies LoadCoValueCoreError);
|
|
701
|
+
return { value: undefined, error: e };
|
|
711
702
|
}
|
|
712
703
|
|
|
713
704
|
if (
|
|
@@ -717,14 +708,15 @@ export class LocalNode {
|
|
|
717
708
|
!("type" in coValue.verified.header.meta) ||
|
|
718
709
|
coValue.verified.header.meta.type !== "account"
|
|
719
710
|
) {
|
|
720
|
-
return
|
|
721
|
-
|
|
722
|
-
expectation,
|
|
723
|
-
|
|
724
|
-
} satisfies UnexpectedlyNotAccountError);
|
|
711
|
+
return {
|
|
712
|
+
value: undefined,
|
|
713
|
+
error: new Error(`Unexpectedly not account: ${expectation}`),
|
|
714
|
+
};
|
|
725
715
|
}
|
|
726
716
|
|
|
727
|
-
|
|
717
|
+
const account = coValue.getCurrentContent() as RawAccount;
|
|
718
|
+
|
|
719
|
+
return { value: account.currentAgentID(), error: undefined };
|
|
728
720
|
}
|
|
729
721
|
|
|
730
722
|
createGroup(
|
package/src/sync.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { PeerState } from "./PeerState.js";
|
|
|
4
4
|
import { SyncStateManager } from "./SyncStateManager.js";
|
|
5
5
|
import {
|
|
6
6
|
getContenDebugInfo,
|
|
7
|
+
getNewTransactionsFromContentMessage,
|
|
8
|
+
getSessionEntriesFromContentMessage,
|
|
7
9
|
getTransactionSize,
|
|
8
10
|
knownStateFromContent,
|
|
9
11
|
} from "./coValueContentMessage.js";
|
|
@@ -609,56 +611,55 @@ export class SyncManager {
|
|
|
609
611
|
/**
|
|
610
612
|
* The coValue is in memory, load the transactions from the content message
|
|
611
613
|
*/
|
|
612
|
-
for (const [
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
]
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
614
|
+
for (const [
|
|
615
|
+
sessionID,
|
|
616
|
+
newContentForSession,
|
|
617
|
+
] of getSessionEntriesFromContentMessage(msg)) {
|
|
618
|
+
const newTransactions = getNewTransactionsFromContentMessage(
|
|
619
|
+
newContentForSession,
|
|
620
|
+
coValue.knownState(),
|
|
621
|
+
sessionID,
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
if (newTransactions === undefined) {
|
|
621
625
|
invalidStateAssumed = true;
|
|
622
626
|
continue;
|
|
623
627
|
}
|
|
624
628
|
|
|
625
|
-
const alreadyKnownOffset = ourKnownTxIdx
|
|
626
|
-
? ourKnownTxIdx - theirFirstNewTxIdx
|
|
627
|
-
: 0;
|
|
628
|
-
|
|
629
|
-
const newTransactions =
|
|
630
|
-
newContentForSession.newTransactions.slice(alreadyKnownOffset);
|
|
631
|
-
|
|
632
629
|
if (newTransactions.length === 0) {
|
|
633
630
|
continue;
|
|
634
631
|
}
|
|
635
632
|
|
|
636
633
|
// TODO: Handle invalid signatures in the middle of streaming
|
|
637
634
|
// This could cause a situation where we are unable to load a chunk, and ask for a correction for all the subsequent chunks
|
|
638
|
-
const
|
|
635
|
+
const error = coValue.tryAddTransactions(
|
|
639
636
|
sessionID,
|
|
640
637
|
newTransactions,
|
|
641
638
|
newContentForSession.lastSignature,
|
|
642
639
|
this.skipVerify,
|
|
643
640
|
);
|
|
644
641
|
|
|
645
|
-
if (
|
|
642
|
+
if (error) {
|
|
646
643
|
if (peer) {
|
|
647
644
|
logger.error("Failed to add transactions", {
|
|
648
645
|
peerId: peer.id,
|
|
649
646
|
peerRole: peer.role,
|
|
650
647
|
id: msg.id,
|
|
651
|
-
|
|
648
|
+
errorType: error.type,
|
|
649
|
+
err: error.error,
|
|
650
|
+
sessionID,
|
|
652
651
|
msgKnownState: knownStateFromContent(msg).sessions,
|
|
652
|
+
msgSummary: getContenDebugInfo(msg),
|
|
653
653
|
knownState: coValue.knownState().sessions,
|
|
654
|
-
newContent: validNewContent.new,
|
|
655
654
|
});
|
|
656
655
|
// TODO Mark only the session as errored, not the whole coValue
|
|
657
|
-
coValue.markErrored(peer.id,
|
|
656
|
+
coValue.markErrored(peer.id, error);
|
|
658
657
|
} else {
|
|
659
658
|
logger.error("Failed to add transactions from storage", {
|
|
660
659
|
id: msg.id,
|
|
661
|
-
err:
|
|
660
|
+
err: error.error,
|
|
661
|
+
sessionID,
|
|
662
|
+
errorType: error.type,
|
|
662
663
|
});
|
|
663
664
|
}
|
|
664
665
|
continue;
|
|
@@ -669,9 +670,7 @@ export class SyncManager {
|
|
|
669
670
|
}
|
|
670
671
|
|
|
671
672
|
// The new content for this session has been verified, so we can store it
|
|
672
|
-
|
|
673
|
-
validNewContent.new[sessionID] = newContentForSession;
|
|
674
|
-
}
|
|
673
|
+
validNewContent.new[sessionID] = newContentForSession;
|
|
675
674
|
}
|
|
676
675
|
|
|
677
676
|
if (peer) {
|