cojson 0.18.26 → 0.18.28
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 +17 -0
- package/dist/PeerKnownStates.d.ts +4 -3
- package/dist/PeerKnownStates.d.ts.map +1 -1
- package/dist/PeerKnownStates.js +27 -18
- package/dist/PeerKnownStates.js.map +1 -1
- package/dist/PeerState.d.ts +3 -2
- package/dist/PeerState.d.ts.map +1 -1
- package/dist/PeerState.js.map +1 -1
- package/dist/SyncStateManager.d.ts +2 -2
- package/dist/SyncStateManager.d.ts.map +1 -1
- package/dist/SyncStateManager.js +2 -10
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/coValueContentMessage.d.ts +1 -1
- package/dist/coValueContentMessage.d.ts.map +1 -1
- package/dist/coValueContentMessage.js +1 -1
- package/dist/coValueContentMessage.js.map +1 -1
- package/dist/coValueCore/SessionMap.d.ts +6 -3
- package/dist/coValueCore/SessionMap.d.ts.map +1 -1
- package/dist/coValueCore/SessionMap.js +41 -8
- package/dist/coValueCore/SessionMap.js.map +1 -1
- package/dist/coValueCore/branching.d.ts +5 -4
- package/dist/coValueCore/branching.d.ts.map +1 -1
- package/dist/coValueCore/branching.js +22 -4
- package/dist/coValueCore/branching.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +29 -25
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +163 -126
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/decryptTransactionChangesAndMeta.d.ts +3 -0
- package/dist/coValueCore/decryptTransactionChangesAndMeta.d.ts.map +1 -0
- package/dist/coValueCore/decryptTransactionChangesAndMeta.js +34 -0
- package/dist/coValueCore/decryptTransactionChangesAndMeta.js.map +1 -0
- package/dist/coValueCore/verifiedState.d.ts +12 -6
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValueCore/verifiedState.js +28 -56
- package/dist/coValueCore/verifiedState.js.map +1 -1
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.d.ts.map +1 -1
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/exports.d.ts +3 -2
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +2 -1
- package/dist/exports.js.map +1 -1
- package/dist/knownState.d.ts +58 -2
- package/dist/knownState.d.ts.map +1 -1
- package/dist/knownState.js +79 -5
- package/dist/knownState.js.map +1 -1
- package/dist/localNode.js +1 -1
- package/dist/localNode.js.map +1 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +18 -20
- package/dist/permissions.js.map +1 -1
- package/dist/storage/knownState.d.ts +1 -1
- package/dist/storage/knownState.d.ts.map +1 -1
- package/dist/storage/knownState.js +2 -3
- package/dist/storage/knownState.js.map +1 -1
- package/dist/storage/storageAsync.d.ts +2 -1
- package/dist/storage/storageAsync.d.ts.map +1 -1
- package/dist/storage/storageAsync.js +5 -6
- package/dist/storage/storageAsync.js.map +1 -1
- package/dist/storage/storageSync.d.ts +2 -1
- package/dist/storage/storageSync.d.ts.map +1 -1
- package/dist/storage/storageSync.js +5 -5
- package/dist/storage/storageSync.js.map +1 -1
- package/dist/storage/types.d.ts +2 -1
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/sync.d.ts +2 -12
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +9 -38
- package/dist/sync.js.map +1 -1
- package/dist/tests/PeerKnownStates.test.js +1 -1
- package/dist/tests/PeerKnownStates.test.js.map +1 -1
- package/dist/tests/PeerState.test.js +19 -0
- package/dist/tests/PeerState.test.js.map +1 -1
- package/dist/tests/PureJSCrypto.test.js.map +1 -1
- package/dist/tests/StorageApiAsync.test.js +1 -1
- package/dist/tests/StorageApiAsync.test.js.map +1 -1
- package/dist/tests/StorageApiSync.test.js +1 -2
- package/dist/tests/StorageApiSync.test.js.map +1 -1
- package/dist/tests/StoreQueue.test.js.map +1 -1
- package/dist/tests/SyncStateManager.test.js +1 -1
- package/dist/tests/SyncStateManager.test.js.map +1 -1
- package/dist/tests/branching.test.js +237 -28
- package/dist/tests/branching.test.js.map +1 -1
- package/dist/tests/coValueContentMessage.test.js +1 -1
- package/dist/tests/coValueContentMessage.test.js.map +1 -1
- package/dist/tests/coValueCore.loadFromStorage.test.d.ts +2 -0
- package/dist/tests/coValueCore.loadFromStorage.test.d.ts.map +1 -0
- package/dist/tests/coValueCore.loadFromStorage.test.js +395 -0
- package/dist/tests/coValueCore.loadFromStorage.test.js.map +1 -0
- package/dist/tests/coValueCore.loadingState.test.d.ts +2 -0
- package/dist/tests/coValueCore.loadingState.test.d.ts.map +1 -0
- package/dist/tests/{coValueCoreLoadingState.test.js → coValueCore.loadingState.test.js} +4 -12
- package/dist/tests/coValueCore.loadingState.test.js.map +1 -0
- package/dist/tests/coValueCore.test.js +30 -5
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/knownState.test.d.ts +2 -0
- package/dist/tests/knownState.test.d.ts.map +1 -0
- package/dist/tests/knownState.test.js +510 -0
- package/dist/tests/knownState.test.js.map +1 -0
- package/dist/tests/messagesTestUtils.d.ts.map +1 -1
- package/dist/tests/messagesTestUtils.js.map +1 -1
- package/dist/tests/priority.test.js.map +1 -1
- package/dist/tests/sync.mesh.test.js +4 -34
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.upload.test.js.map +1 -1
- package/dist/tests/testUtils.d.ts +7 -1
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +12 -0
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +3 -3
- package/src/PeerKnownStates.ts +36 -22
- package/src/PeerState.ts +2 -1
- package/src/SyncStateManager.ts +4 -17
- package/src/coValueContentMessage.ts +2 -1
- package/src/coValueCore/SessionMap.ts +66 -11
- package/src/coValueCore/branching.ts +37 -13
- package/src/coValueCore/coValueCore.ts +224 -177
- package/src/coValueCore/decryptTransactionChangesAndMeta.ts +56 -0
- package/src/coValueCore/verifiedState.ts +32 -64
- package/src/coValues/coMap.ts +1 -5
- package/src/coValues/coStream.ts +1 -5
- package/src/exports.ts +2 -2
- package/src/knownState.ts +118 -12
- package/src/localNode.ts +1 -1
- package/src/permissions.ts +21 -22
- package/src/storage/knownState.ts +9 -3
- package/src/storage/storageAsync.ts +15 -7
- package/src/storage/storageSync.ts +16 -8
- package/src/storage/types.ts +2 -1
- package/src/sync.ts +14 -60
- package/src/tests/PeerKnownStates.test.ts +1 -1
- package/src/tests/PeerState.test.ts +29 -3
- package/src/tests/PureJSCrypto.test.ts +0 -1
- package/src/tests/StorageApiAsync.test.ts +3 -6
- package/src/tests/StorageApiSync.test.ts +2 -6
- package/src/tests/StoreQueue.test.ts +3 -2
- package/src/tests/SyncStateManager.test.ts +1 -1
- package/src/tests/branching.test.ts +392 -45
- package/src/tests/coValueContentMessage.test.ts +2 -2
- package/src/tests/coValueCore.loadFromStorage.test.ts +540 -0
- package/src/tests/{coValueCoreLoadingState.test.ts → coValueCore.loadingState.test.ts} +3 -16
- package/src/tests/coValueCore.test.ts +40 -5
- package/src/tests/knownState.test.ts +665 -0
- package/src/tests/messagesTestUtils.ts +2 -1
- package/src/tests/priority.test.ts +0 -2
- package/src/tests/sync.mesh.test.ts +11 -38
- package/src/tests/sync.storage.test.ts +0 -1
- package/src/tests/sync.upload.test.ts +0 -1
- package/src/tests/testUtils.ts +22 -2
- package/dist/coValueCore/decodeTransactionChangesAndMeta.d.ts +0 -3
- package/dist/coValueCore/decodeTransactionChangesAndMeta.d.ts.map +0 -1
- package/dist/coValueCore/decodeTransactionChangesAndMeta.js +0 -59
- package/dist/coValueCore/decodeTransactionChangesAndMeta.js.map +0 -1
- package/dist/tests/coValueCoreLoadingState.test.d.ts +0 -2
- package/dist/tests/coValueCoreLoadingState.test.d.ts.map +0 -1
- package/dist/tests/coValueCoreLoadingState.test.js.map +0 -1
- package/src/coValueCore/decodeTransactionChangesAndMeta.ts +0 -81
|
@@ -20,12 +20,7 @@ import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
|
20
20
|
import { LocalNode, ResolveAccountAgentError } from "../localNode.js";
|
|
21
21
|
import { logger } from "../logger.js";
|
|
22
22
|
import { determineValidTransactions } from "../permissions.js";
|
|
23
|
-
import {
|
|
24
|
-
CoValueKnownState,
|
|
25
|
-
NewContentMessage,
|
|
26
|
-
PeerID,
|
|
27
|
-
emptyKnownState,
|
|
28
|
-
} from "../sync.js";
|
|
23
|
+
import { NewContentMessage, PeerID } from "../sync.js";
|
|
29
24
|
import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
|
|
30
25
|
import { expectGroup } from "../typeUtils/expectGroup.js";
|
|
31
26
|
import {
|
|
@@ -47,8 +42,14 @@ import {
|
|
|
47
42
|
BranchStartCommit,
|
|
48
43
|
} from "./branching.js";
|
|
49
44
|
import { type RawAccountID } from "../coValues/account.js";
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
45
|
+
import { decryptTransactionChangesAndMeta } from "./decryptTransactionChangesAndMeta.js";
|
|
46
|
+
import {
|
|
47
|
+
combineKnownStateSessions,
|
|
48
|
+
CoValueKnownState,
|
|
49
|
+
emptyKnownState,
|
|
50
|
+
KnownStateSessions,
|
|
51
|
+
} from "../knownState.js";
|
|
52
|
+
import { safeParseJSON } from "../jsonStringify.js";
|
|
52
53
|
|
|
53
54
|
export function idforHeader(
|
|
54
55
|
header: CoValueHeader,
|
|
@@ -58,41 +59,102 @@ export function idforHeader(
|
|
|
58
59
|
return `co_z${hash.slice("shortHash_z".length)}`;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
export
|
|
62
|
+
export class VerifiedTransaction {
|
|
62
63
|
// The account or agent that made the transaction
|
|
63
64
|
author: RawAccountID | AgentID;
|
|
64
65
|
// An object containing the session ID and the transaction index
|
|
65
|
-
|
|
66
|
+
currentTxID: TransactionID;
|
|
67
|
+
// If this is a merged transaction, the TxID of the transaction inside the original branch
|
|
68
|
+
sourceTxID: TransactionID | undefined;
|
|
66
69
|
tx: Transaction;
|
|
67
70
|
// The Unix time when the transaction was made
|
|
68
|
-
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
+
currentMadeAt: number;
|
|
72
|
+
// If this is a merged transaction, the madeAt of the transaction inside the original branch
|
|
73
|
+
sourceTxMadeAt: number | undefined;
|
|
71
74
|
// The decoded changes of the transaction
|
|
72
75
|
changes: JsonValue[] | undefined;
|
|
73
76
|
// The decoded meta information of the transaction
|
|
74
77
|
meta: JsonObject | undefined;
|
|
75
|
-
|
|
76
78
|
// Whether the transaction is valid, as per membership rules
|
|
77
|
-
isValid: boolean;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
hasInvalidMeta: boolean;
|
|
83
|
-
|
|
79
|
+
isValid: boolean = false;
|
|
80
|
+
// Whether the transaction has been validated, used to track if determinedValidTransactions needs to check this
|
|
81
|
+
isValidated: boolean = false;
|
|
82
|
+
// True if the transaction has been decrypted
|
|
83
|
+
isDecrypted: boolean = false;
|
|
84
84
|
// True if the meta information has been parsed and loaded in the CoValueCore
|
|
85
|
-
hasMetaBeenParsed: boolean;
|
|
86
|
-
|
|
85
|
+
hasMetaBeenParsed: boolean = false;
|
|
87
86
|
// The previous verified transaction for the same session
|
|
88
87
|
previous: VerifiedTransaction | undefined;
|
|
89
|
-
};
|
|
90
88
|
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
constructor(
|
|
90
|
+
sessionID: SessionID,
|
|
91
|
+
txIndex: number,
|
|
92
|
+
tx: Transaction,
|
|
93
|
+
branchId: RawCoID | undefined,
|
|
94
|
+
parsingCache:
|
|
95
|
+
| { changes: JsonValue[]; meta: JsonObject | undefined }
|
|
96
|
+
| undefined,
|
|
97
|
+
previous: VerifiedTransaction | undefined,
|
|
98
|
+
) {
|
|
99
|
+
this.author = accountOrAgentIDfromSessionID(sessionID);
|
|
100
|
+
|
|
101
|
+
const txID = branchId
|
|
102
|
+
? {
|
|
103
|
+
sessionID,
|
|
104
|
+
txIndex,
|
|
105
|
+
branch: branchId,
|
|
106
|
+
}
|
|
107
|
+
: {
|
|
108
|
+
sessionID,
|
|
109
|
+
txIndex,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
this.currentTxID = txID;
|
|
113
|
+
this.sourceTxID = undefined;
|
|
114
|
+
this.tx = tx;
|
|
115
|
+
this.currentMadeAt = tx.madeAt;
|
|
116
|
+
this.sourceTxMadeAt = undefined;
|
|
117
|
+
this.isValidated = false;
|
|
118
|
+
|
|
119
|
+
this.previous = previous;
|
|
120
|
+
|
|
121
|
+
if (parsingCache) {
|
|
122
|
+
this.changes = parsingCache.changes;
|
|
123
|
+
this.meta = parsingCache.meta;
|
|
124
|
+
} else {
|
|
125
|
+
// Decoding the trusting transactions here because they might be useful in the permissions checks
|
|
126
|
+
if (this.tx.privacy === "trusting") {
|
|
127
|
+
this.changes = safeParseJSON(this.tx.changes);
|
|
128
|
+
|
|
129
|
+
if (this.tx.meta) {
|
|
130
|
+
this.meta = safeParseJSON(this.tx.meta);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// The TxID that refers to the current position in the session map
|
|
137
|
+
// If this is a merged transaction, the txID is the TxID of the merged transaction
|
|
138
|
+
get txID() {
|
|
139
|
+
return this.sourceTxID ?? this.currentTxID;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// The madeAt that refers to the time when the transaction was made
|
|
143
|
+
// If this is a merged transaction, the madeAt is the time when the transaction has been made in the branch
|
|
144
|
+
get madeAt() {
|
|
145
|
+
return this.sourceTxMadeAt ?? this.currentMadeAt;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
isValidTransactionWithChanges(): this is {
|
|
149
|
+
changes: JsonValue[];
|
|
150
|
+
isValid: true;
|
|
151
|
+
} {
|
|
152
|
+
return Boolean(this.isValid && this.changes);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export type DecryptedTransaction = Omit<VerifiedTransaction, "changes"> & {
|
|
93
157
|
changes: JsonValue[];
|
|
94
|
-
madeAt: number;
|
|
95
|
-
tx: Transaction;
|
|
96
158
|
};
|
|
97
159
|
|
|
98
160
|
export type AvailableCoValueCore = CoValueCore & { verified: VerifiedState };
|
|
@@ -119,8 +181,9 @@ export class CoValueCore {
|
|
|
119
181
|
get verified() {
|
|
120
182
|
return this._verified;
|
|
121
183
|
}
|
|
122
|
-
|
|
123
|
-
|
|
184
|
+
|
|
185
|
+
private readonly loadingStatuses = new Map<
|
|
186
|
+
PeerID | "storage",
|
|
124
187
|
| {
|
|
125
188
|
type: "unknown" | "pending" | "available" | "unavailable";
|
|
126
189
|
}
|
|
@@ -136,18 +199,10 @@ export class CoValueCore {
|
|
|
136
199
|
new Set();
|
|
137
200
|
private counter: UpDownCounter;
|
|
138
201
|
|
|
139
|
-
|
|
140
|
-
init: { header: CoValueHeader } | { id: RawCoID },
|
|
141
|
-
node: LocalNode,
|
|
142
|
-
) {
|
|
202
|
+
constructor(id: RawCoID, node: LocalNode) {
|
|
143
203
|
this.crypto = node.crypto;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
this._verified = new VerifiedState(this.id, node.crypto, init.header);
|
|
147
|
-
} else {
|
|
148
|
-
this.id = init.id;
|
|
149
|
-
this._verified = null;
|
|
150
|
-
}
|
|
204
|
+
this.id = id;
|
|
205
|
+
this._verified = null;
|
|
151
206
|
this.node = node;
|
|
152
207
|
|
|
153
208
|
this.counter = metrics
|
|
@@ -161,25 +216,14 @@ export class CoValueCore {
|
|
|
161
216
|
this.updateCounter(null);
|
|
162
217
|
}
|
|
163
218
|
|
|
164
|
-
static fromID(id: RawCoID, node: LocalNode): CoValueCore {
|
|
165
|
-
return new CoValueCore({ id }, node);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
static fromHeader(
|
|
169
|
-
header: CoValueHeader,
|
|
170
|
-
node: LocalNode,
|
|
171
|
-
): AvailableCoValueCore {
|
|
172
|
-
return new CoValueCore({ header }, node) as AvailableCoValueCore;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
219
|
get loadingState() {
|
|
176
220
|
if (this.verified) {
|
|
177
221
|
return "available";
|
|
178
|
-
} else if (this.
|
|
222
|
+
} else if (this.loadingStatuses.size === 0) {
|
|
179
223
|
return "unknown";
|
|
180
224
|
}
|
|
181
225
|
|
|
182
|
-
for (const peer of this.
|
|
226
|
+
for (const peer of this.loadingStatuses.values()) {
|
|
183
227
|
if (peer.type === "pending") {
|
|
184
228
|
return "loading";
|
|
185
229
|
} else if (peer.type === "unknown") {
|
|
@@ -203,53 +247,38 @@ export class CoValueCore {
|
|
|
203
247
|
}
|
|
204
248
|
|
|
205
249
|
isErroredInPeer(peerId: PeerID) {
|
|
206
|
-
return this.
|
|
250
|
+
return this.getLoadingStateForPeer(peerId) === "errored";
|
|
207
251
|
}
|
|
208
252
|
|
|
209
|
-
|
|
253
|
+
waitFor(callback: (value: CoValueCore) => boolean) {
|
|
210
254
|
return new Promise<CoValueCore>((resolve) => {
|
|
211
|
-
|
|
212
|
-
if (
|
|
255
|
+
this.subscribe((core, unsubscribe) => {
|
|
256
|
+
if (callback(core)) {
|
|
257
|
+
unsubscribe();
|
|
213
258
|
resolve(core);
|
|
214
|
-
this.listeners.delete(listener);
|
|
215
259
|
}
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
this.listeners.add(listener);
|
|
219
|
-
listener(this);
|
|
260
|
+
}, true);
|
|
220
261
|
});
|
|
221
262
|
}
|
|
222
263
|
|
|
223
|
-
|
|
224
|
-
return
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
this.listeners.delete(listener);
|
|
229
|
-
}
|
|
230
|
-
};
|
|
264
|
+
waitForAvailableOrUnavailable(): Promise<CoValueCore> {
|
|
265
|
+
return this.waitFor(
|
|
266
|
+
(core) => core.isAvailable() || core.loadingState === "unavailable",
|
|
267
|
+
);
|
|
268
|
+
}
|
|
231
269
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
});
|
|
270
|
+
waitForAvailable(): Promise<CoValueCore> {
|
|
271
|
+
return this.waitFor((core) => core.isAvailable());
|
|
235
272
|
}
|
|
236
273
|
|
|
237
274
|
waitForFullStreaming(): Promise<CoValueCore> {
|
|
238
|
-
return
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
resolve(core);
|
|
242
|
-
this.listeners.delete(listener);
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
this.listeners.add(listener);
|
|
247
|
-
listener(this);
|
|
248
|
-
});
|
|
275
|
+
return this.waitFor(
|
|
276
|
+
(core) => core.isAvailable() && !core.verified.isStreaming(),
|
|
277
|
+
);
|
|
249
278
|
}
|
|
250
279
|
|
|
251
|
-
|
|
252
|
-
return this.
|
|
280
|
+
getLoadingStateForPeer(peerId: PeerID) {
|
|
281
|
+
return this.loadingStatuses.get(peerId)?.type ?? "unknown";
|
|
253
282
|
}
|
|
254
283
|
|
|
255
284
|
private updateCounter(previousState: string | null) {
|
|
@@ -289,13 +318,13 @@ export class CoValueCore {
|
|
|
289
318
|
|
|
290
319
|
markNotFoundInPeer(peerId: PeerID) {
|
|
291
320
|
const previousState = this.loadingState;
|
|
292
|
-
this.
|
|
321
|
+
this.loadingStatuses.set(peerId, { type: "unavailable" });
|
|
293
322
|
this.updateCounter(previousState);
|
|
294
323
|
this.scheduleNotifyUpdate();
|
|
295
324
|
}
|
|
296
325
|
|
|
297
326
|
markFoundInPeer(peerId: PeerID, previousState: string) {
|
|
298
|
-
this.
|
|
327
|
+
this.loadingStatuses.set(peerId, { type: "available" });
|
|
299
328
|
this.updateCounter(previousState);
|
|
300
329
|
this.scheduleNotifyUpdate();
|
|
301
330
|
}
|
|
@@ -378,7 +407,7 @@ export class CoValueCore {
|
|
|
378
407
|
|
|
379
408
|
provideHeader(
|
|
380
409
|
header: CoValueHeader,
|
|
381
|
-
streamingKnownState?:
|
|
410
|
+
streamingKnownState?: KnownStateSessions,
|
|
382
411
|
skipVerify?: boolean,
|
|
383
412
|
) {
|
|
384
413
|
if (!skipVerify) {
|
|
@@ -400,8 +429,7 @@ export class CoValueCore {
|
|
|
400
429
|
this.id,
|
|
401
430
|
this.node.crypto,
|
|
402
431
|
header,
|
|
403
|
-
new SessionMap(this.id, this.node.crypto),
|
|
404
|
-
streamingKnownState,
|
|
432
|
+
new SessionMap(this.id, this.node.crypto, streamingKnownState),
|
|
405
433
|
);
|
|
406
434
|
|
|
407
435
|
return true;
|
|
@@ -409,14 +437,14 @@ export class CoValueCore {
|
|
|
409
437
|
|
|
410
438
|
markErrored(peerId: PeerID, error: TryAddTransactionsError) {
|
|
411
439
|
const previousState = this.loadingState;
|
|
412
|
-
this.
|
|
440
|
+
this.loadingStatuses.set(peerId, { type: "errored", error });
|
|
413
441
|
this.updateCounter(previousState);
|
|
414
442
|
this.scheduleNotifyUpdate();
|
|
415
443
|
}
|
|
416
444
|
|
|
417
445
|
markPending(peerId: PeerID) {
|
|
418
446
|
const previousState = this.loadingState;
|
|
419
|
-
this.
|
|
447
|
+
this.loadingStatuses.set(peerId, { type: "pending" });
|
|
420
448
|
this.updateCounter(previousState);
|
|
421
449
|
this.scheduleNotifyUpdate();
|
|
422
450
|
}
|
|
@@ -815,36 +843,19 @@ export class CoValueCore {
|
|
|
815
843
|
continue;
|
|
816
844
|
}
|
|
817
845
|
|
|
818
|
-
const txID = isBranched
|
|
819
|
-
? {
|
|
820
|
-
sessionID,
|
|
821
|
-
txIndex,
|
|
822
|
-
branch: this.id,
|
|
823
|
-
}
|
|
824
|
-
: {
|
|
825
|
-
sessionID,
|
|
826
|
-
txIndex,
|
|
827
|
-
};
|
|
828
|
-
|
|
829
846
|
const cache = this.parsingCache.get(tx);
|
|
830
847
|
if (cache) {
|
|
831
848
|
this.parsingCache.delete(tx);
|
|
832
849
|
}
|
|
833
850
|
|
|
834
|
-
const verifiedTransaction =
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
madeAt: tx.madeAt,
|
|
838
|
-
isValidated: false,
|
|
839
|
-
isValid: false,
|
|
840
|
-
changes: cache?.changes,
|
|
841
|
-
meta: cache?.meta,
|
|
842
|
-
hasInvalidChanges: false,
|
|
843
|
-
hasInvalidMeta: false,
|
|
844
|
-
hasMetaBeenParsed: false,
|
|
851
|
+
const verifiedTransaction = new VerifiedTransaction(
|
|
852
|
+
sessionID,
|
|
853
|
+
txIndex,
|
|
845
854
|
tx,
|
|
846
|
-
|
|
847
|
-
|
|
855
|
+
isBranched ? this.id : undefined,
|
|
856
|
+
cache,
|
|
857
|
+
this.lastVerifiedTransactionBySessionID[sessionID],
|
|
858
|
+
);
|
|
848
859
|
|
|
849
860
|
if (verifiedTransaction.madeAt > this.latestTxMadeAt) {
|
|
850
861
|
this.latestTxMadeAt = verifiedTransaction.madeAt;
|
|
@@ -920,19 +931,33 @@ export class CoValueCore {
|
|
|
920
931
|
const meta = transaction.meta as MergedTransactionMetadata;
|
|
921
932
|
|
|
922
933
|
// Check if the transaction is a merge commit
|
|
923
|
-
const previousTransaction = transaction.previous
|
|
924
|
-
const sessionID = meta.s ?? previousTransaction?.sessionID;
|
|
934
|
+
const previousTransaction = transaction.previous;
|
|
935
|
+
const sessionID = meta.s ?? previousTransaction?.txID.sessionID;
|
|
936
|
+
|
|
937
|
+
if (meta.t) {
|
|
938
|
+
transaction.sourceTxMadeAt = transaction.currentMadeAt - meta.t;
|
|
939
|
+
} else if (previousTransaction) {
|
|
940
|
+
transaction.sourceTxMadeAt = previousTransaction.madeAt;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Check against tampering of the meta.t value to write transaction after the access revocation
|
|
944
|
+
if (
|
|
945
|
+
transaction.sourceTxMadeAt &&
|
|
946
|
+
transaction.sourceTxMadeAt > transaction.currentMadeAt
|
|
947
|
+
) {
|
|
948
|
+
transaction.isValid = false;
|
|
949
|
+
}
|
|
925
950
|
|
|
926
951
|
if (sessionID) {
|
|
927
|
-
transaction.
|
|
952
|
+
transaction.sourceTxID = {
|
|
928
953
|
sessionID,
|
|
929
954
|
txIndex: meta.mi,
|
|
930
|
-
branch: meta.b ?? previousTransaction?.branch,
|
|
955
|
+
branch: meta.b ?? previousTransaction?.txID.branch,
|
|
931
956
|
};
|
|
932
957
|
} else {
|
|
933
958
|
logger.error("Merge commit without session ID", {
|
|
934
959
|
txID: transaction.txID,
|
|
935
|
-
|
|
960
|
+
prevTxID: previousTransaction?.txID ?? null,
|
|
936
961
|
});
|
|
937
962
|
}
|
|
938
963
|
}
|
|
@@ -953,11 +978,10 @@ export class CoValueCore {
|
|
|
953
978
|
this.determineValidTransactions();
|
|
954
979
|
|
|
955
980
|
for (const transaction of this.verifiedTransactions) {
|
|
956
|
-
|
|
957
|
-
this,
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
);
|
|
981
|
+
if (!ignorePrivateTransactions) {
|
|
982
|
+
decryptTransactionChangesAndMeta(this, transaction);
|
|
983
|
+
}
|
|
984
|
+
|
|
961
985
|
this.parseMetaInformation(transaction);
|
|
962
986
|
}
|
|
963
987
|
}
|
|
@@ -988,7 +1012,7 @@ export class CoValueCore {
|
|
|
988
1012
|
const source = getBranchSource(this);
|
|
989
1013
|
|
|
990
1014
|
for (const transaction of this.verifiedTransactions) {
|
|
991
|
-
if (!isValidTransactionWithChanges(
|
|
1015
|
+
if (!transaction.isValidTransactionWithChanges()) {
|
|
992
1016
|
continue;
|
|
993
1017
|
}
|
|
994
1018
|
|
|
@@ -998,7 +1022,8 @@ export class CoValueCore {
|
|
|
998
1022
|
|
|
999
1023
|
options?.knownTransactions?.add(transaction.tx);
|
|
1000
1024
|
|
|
1001
|
-
|
|
1025
|
+
// Using the currentTxID to filter the transactions, because the TxID is modified by the merge meta
|
|
1026
|
+
const txID = transaction.currentTxID;
|
|
1002
1027
|
|
|
1003
1028
|
const from = options?.from?.[txID.sessionID] ?? -1;
|
|
1004
1029
|
|
|
@@ -1122,8 +1147,8 @@ export class CoValueCore {
|
|
|
1122
1147
|
}
|
|
1123
1148
|
|
|
1124
1149
|
compareTransactions(
|
|
1125
|
-
a: Pick<
|
|
1126
|
-
b: Pick<
|
|
1150
|
+
a: Pick<VerifiedTransaction, "madeAt" | "txID">,
|
|
1151
|
+
b: Pick<VerifiedTransaction, "madeAt" | "txID">,
|
|
1127
1152
|
) {
|
|
1128
1153
|
if (a.madeAt !== b.madeAt) {
|
|
1129
1154
|
return a.madeAt - b.madeAt;
|
|
@@ -1196,7 +1221,7 @@ export class CoValueCore {
|
|
|
1196
1221
|
}
|
|
1197
1222
|
}
|
|
1198
1223
|
|
|
1199
|
-
|
|
1224
|
+
safeGetGroup(): RawGroup | undefined {
|
|
1200
1225
|
if (!this.verified) {
|
|
1201
1226
|
throw new Error(
|
|
1202
1227
|
"CoValueCore: getGroup called on coValue without verified state",
|
|
@@ -1204,7 +1229,7 @@ export class CoValueCore {
|
|
|
1204
1229
|
}
|
|
1205
1230
|
|
|
1206
1231
|
if (this.verified.header.ruleset.type !== "ownedByGroup") {
|
|
1207
|
-
|
|
1232
|
+
return undefined;
|
|
1208
1233
|
}
|
|
1209
1234
|
|
|
1210
1235
|
return expectGroup(
|
|
@@ -1214,6 +1239,16 @@ export class CoValueCore {
|
|
|
1214
1239
|
);
|
|
1215
1240
|
}
|
|
1216
1241
|
|
|
1242
|
+
getGroup(): RawGroup {
|
|
1243
|
+
const group = this.safeGetGroup();
|
|
1244
|
+
|
|
1245
|
+
if (!group) {
|
|
1246
|
+
throw new Error("Only values owned by groups have groups");
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
return group;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1217
1252
|
getTx(txID: TransactionID): Transaction | undefined {
|
|
1218
1253
|
return this.verified?.sessions.get(txID.sessionID)?.transactions[
|
|
1219
1254
|
txID.txIndex
|
|
@@ -1245,10 +1280,34 @@ export class CoValueCore {
|
|
|
1245
1280
|
return;
|
|
1246
1281
|
}
|
|
1247
1282
|
|
|
1248
|
-
const currentState = this.
|
|
1283
|
+
const currentState = this.getLoadingStateForPeer("storage");
|
|
1284
|
+
|
|
1285
|
+
if (currentState === "pending") {
|
|
1286
|
+
if (!done) {
|
|
1287
|
+
// We don't need to notify the result to anyone, so we can return early
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// Loading the value
|
|
1292
|
+
this.subscribe((state, unsubscribe) => {
|
|
1293
|
+
const updatedState = state.getLoadingStateForPeer("storage");
|
|
1249
1294
|
|
|
1250
|
-
|
|
1251
|
-
|
|
1295
|
+
if (updatedState === "available" || state.isAvailable()) {
|
|
1296
|
+
unsubscribe();
|
|
1297
|
+
done(true);
|
|
1298
|
+
} else if (
|
|
1299
|
+
updatedState === "errored" ||
|
|
1300
|
+
updatedState === "unavailable"
|
|
1301
|
+
) {
|
|
1302
|
+
unsubscribe();
|
|
1303
|
+
done(false);
|
|
1304
|
+
}
|
|
1305
|
+
});
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
if (currentState !== "unknown") {
|
|
1310
|
+
done?.(currentState === "available");
|
|
1252
1311
|
return;
|
|
1253
1312
|
}
|
|
1254
1313
|
|
|
@@ -1274,7 +1333,7 @@ export class CoValueCore {
|
|
|
1274
1333
|
}
|
|
1275
1334
|
|
|
1276
1335
|
for (const peer of peers) {
|
|
1277
|
-
const currentState = this.
|
|
1336
|
+
const currentState = this.getLoadingStateForPeer(peer.id);
|
|
1278
1337
|
|
|
1279
1338
|
if (currentState === "unknown" || currentState === "unavailable") {
|
|
1280
1339
|
this.markPending(peer.id);
|
|
@@ -1301,40 +1360,34 @@ export class CoValueCore {
|
|
|
1301
1360
|
peer.trackLoadRequestSent(this.id);
|
|
1302
1361
|
}
|
|
1303
1362
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
};
|
|
1314
|
-
|
|
1315
|
-
const timeout = setTimeout(markNotFound, CO_VALUE_LOADING_CONFIG.TIMEOUT);
|
|
1316
|
-
const removeCloseListener = peer.persistent
|
|
1317
|
-
? undefined
|
|
1318
|
-
: peer.addCloseListener(markNotFound);
|
|
1319
|
-
|
|
1320
|
-
const listener = (state: CoValueCore) => {
|
|
1321
|
-
const peerState = state.peers.get(peer.id);
|
|
1322
|
-
if (
|
|
1323
|
-
state.isAvailable() || // might have become available from another peer e.g. through handleNewContent
|
|
1324
|
-
peerState?.type === "available" ||
|
|
1325
|
-
peerState?.type === "errored" ||
|
|
1326
|
-
peerState?.type === "unavailable"
|
|
1327
|
-
) {
|
|
1328
|
-
this.listeners.delete(listener);
|
|
1329
|
-
removeCloseListener?.();
|
|
1330
|
-
clearTimeout(timeout);
|
|
1331
|
-
resolve();
|
|
1332
|
-
}
|
|
1333
|
-
};
|
|
1363
|
+
const markNotFound = () => {
|
|
1364
|
+
if (this.getLoadingStateForPeer(peer.id) === "pending") {
|
|
1365
|
+
logger.warn("Timeout waiting for peer to load coValue", {
|
|
1366
|
+
id: this.id,
|
|
1367
|
+
peerID: peer.id,
|
|
1368
|
+
});
|
|
1369
|
+
this.markNotFoundInPeer(peer.id);
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1334
1372
|
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1373
|
+
const timeout = setTimeout(markNotFound, CO_VALUE_LOADING_CONFIG.TIMEOUT);
|
|
1374
|
+
const removeCloseListener = peer.persistent
|
|
1375
|
+
? undefined
|
|
1376
|
+
: peer.addCloseListener(markNotFound);
|
|
1377
|
+
|
|
1378
|
+
this.subscribe((state, unsubscribe) => {
|
|
1379
|
+
const peerState = state.getLoadingStateForPeer(peer.id);
|
|
1380
|
+
if (
|
|
1381
|
+
state.isAvailable() || // might have become available from another peer e.g. through handleNewContent
|
|
1382
|
+
peerState === "available" ||
|
|
1383
|
+
peerState === "errored" ||
|
|
1384
|
+
peerState === "unavailable"
|
|
1385
|
+
) {
|
|
1386
|
+
unsubscribe();
|
|
1387
|
+
removeCloseListener?.();
|
|
1388
|
+
clearTimeout(timeout);
|
|
1389
|
+
}
|
|
1390
|
+
}, true);
|
|
1338
1391
|
}
|
|
1339
1392
|
}
|
|
1340
1393
|
|
|
@@ -1370,9 +1423,3 @@ export type TryAddTransactionsError =
|
|
|
1370
1423
|
| ResolveAccountAgentError
|
|
1371
1424
|
| InvalidHashError
|
|
1372
1425
|
| InvalidSignatureError;
|
|
1373
|
-
|
|
1374
|
-
function isValidTransactionWithChanges(
|
|
1375
|
-
transaction: VerifiedTransaction,
|
|
1376
|
-
): transaction is VerifiedTransaction & { changes: JsonValue[] } {
|
|
1377
|
-
return Boolean(transaction.isValid && transaction.changes);
|
|
1378
|
-
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { AvailableCoValueCore, VerifiedTransaction } from "./coValueCore.js";
|
|
2
|
+
|
|
3
|
+
export function decryptTransactionChangesAndMeta(
|
|
4
|
+
coValue: AvailableCoValueCore,
|
|
5
|
+
transaction: VerifiedTransaction,
|
|
6
|
+
) {
|
|
7
|
+
if (
|
|
8
|
+
!transaction.isValid ||
|
|
9
|
+
transaction.isDecrypted ||
|
|
10
|
+
transaction.tx.privacy === "trusting" // Trusting transactions are already decrypted
|
|
11
|
+
) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const needsChagesParsing = !transaction.changes;
|
|
16
|
+
const needsMetaParsing = !transaction.meta && transaction.tx.meta;
|
|
17
|
+
|
|
18
|
+
if (!needsChagesParsing && !needsMetaParsing) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const readKey = coValue.getReadKey(transaction.tx.keyUsed);
|
|
23
|
+
|
|
24
|
+
if (!readKey) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (needsChagesParsing) {
|
|
29
|
+
const changes = coValue.verified.decryptTransaction(
|
|
30
|
+
transaction.txID.sessionID,
|
|
31
|
+
transaction.txID.txIndex,
|
|
32
|
+
readKey,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (changes) {
|
|
36
|
+
transaction.changes = changes;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (needsMetaParsing) {
|
|
41
|
+
const meta = coValue.verified.decryptTransactionMeta(
|
|
42
|
+
transaction.txID.sessionID,
|
|
43
|
+
transaction.txID.txIndex,
|
|
44
|
+
readKey,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if (meta) {
|
|
48
|
+
transaction.meta = meta;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// We mark the transaction as decrypted even if the changes or meta have failed to be decrypted
|
|
53
|
+
// This is because, if we successfully extracted the readKey and the decrypt failed once it will always fail
|
|
54
|
+
// so better to log the error (we already do that) and mark the transaction as decrypted
|
|
55
|
+
transaction.isDecrypted = true;
|
|
56
|
+
}
|