cojson 0.18.27 → 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 +10 -0
- package/dist/coValueCore/branching.d.ts +2 -1
- package/dist/coValueCore/branching.d.ts.map +1 -1
- package/dist/coValueCore/branching.js +20 -2
- package/dist/coValueCore/branching.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +26 -23
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +161 -123
- 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/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/sync.js +2 -2
- package/dist/sync.js.map +1 -1
- package/dist/tests/branching.test.js +237 -28
- package/dist/tests/branching.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 +2 -5
- package/dist/tests/coValueCore.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/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/coValueCore/branching.ts +28 -3
- package/src/coValueCore/coValueCore.ts +215 -167
- package/src/coValueCore/decryptTransactionChangesAndMeta.ts +56 -0
- package/src/localNode.ts +1 -1
- package/src/permissions.ts +21 -19
- package/src/sync.ts +2 -2
- package/src/tests/branching.test.ts +392 -45
- 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 +2 -5
- package/src/tests/sync.mesh.test.ts +11 -38
- package/src/tests/testUtils.ts +21 -0
- 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
|
@@ -42,13 +42,14 @@ import {
|
|
|
42
42
|
BranchStartCommit,
|
|
43
43
|
} from "./branching.js";
|
|
44
44
|
import { type RawAccountID } from "../coValues/account.js";
|
|
45
|
-
import {
|
|
45
|
+
import { decryptTransactionChangesAndMeta } from "./decryptTransactionChangesAndMeta.js";
|
|
46
46
|
import {
|
|
47
47
|
combineKnownStateSessions,
|
|
48
48
|
CoValueKnownState,
|
|
49
49
|
emptyKnownState,
|
|
50
50
|
KnownStateSessions,
|
|
51
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
|
}
|
|
@@ -408,14 +437,14 @@ export class CoValueCore {
|
|
|
408
437
|
|
|
409
438
|
markErrored(peerId: PeerID, error: TryAddTransactionsError) {
|
|
410
439
|
const previousState = this.loadingState;
|
|
411
|
-
this.
|
|
440
|
+
this.loadingStatuses.set(peerId, { type: "errored", error });
|
|
412
441
|
this.updateCounter(previousState);
|
|
413
442
|
this.scheduleNotifyUpdate();
|
|
414
443
|
}
|
|
415
444
|
|
|
416
445
|
markPending(peerId: PeerID) {
|
|
417
446
|
const previousState = this.loadingState;
|
|
418
|
-
this.
|
|
447
|
+
this.loadingStatuses.set(peerId, { type: "pending" });
|
|
419
448
|
this.updateCounter(previousState);
|
|
420
449
|
this.scheduleNotifyUpdate();
|
|
421
450
|
}
|
|
@@ -814,36 +843,19 @@ export class CoValueCore {
|
|
|
814
843
|
continue;
|
|
815
844
|
}
|
|
816
845
|
|
|
817
|
-
const txID = isBranched
|
|
818
|
-
? {
|
|
819
|
-
sessionID,
|
|
820
|
-
txIndex,
|
|
821
|
-
branch: this.id,
|
|
822
|
-
}
|
|
823
|
-
: {
|
|
824
|
-
sessionID,
|
|
825
|
-
txIndex,
|
|
826
|
-
};
|
|
827
|
-
|
|
828
846
|
const cache = this.parsingCache.get(tx);
|
|
829
847
|
if (cache) {
|
|
830
848
|
this.parsingCache.delete(tx);
|
|
831
849
|
}
|
|
832
850
|
|
|
833
|
-
const verifiedTransaction =
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
madeAt: tx.madeAt,
|
|
837
|
-
isValidated: false,
|
|
838
|
-
isValid: false,
|
|
839
|
-
changes: cache?.changes,
|
|
840
|
-
meta: cache?.meta,
|
|
841
|
-
hasInvalidChanges: false,
|
|
842
|
-
hasInvalidMeta: false,
|
|
843
|
-
hasMetaBeenParsed: false,
|
|
851
|
+
const verifiedTransaction = new VerifiedTransaction(
|
|
852
|
+
sessionID,
|
|
853
|
+
txIndex,
|
|
844
854
|
tx,
|
|
845
|
-
|
|
846
|
-
|
|
855
|
+
isBranched ? this.id : undefined,
|
|
856
|
+
cache,
|
|
857
|
+
this.lastVerifiedTransactionBySessionID[sessionID],
|
|
858
|
+
);
|
|
847
859
|
|
|
848
860
|
if (verifiedTransaction.madeAt > this.latestTxMadeAt) {
|
|
849
861
|
this.latestTxMadeAt = verifiedTransaction.madeAt;
|
|
@@ -919,19 +931,33 @@ export class CoValueCore {
|
|
|
919
931
|
const meta = transaction.meta as MergedTransactionMetadata;
|
|
920
932
|
|
|
921
933
|
// Check if the transaction is a merge commit
|
|
922
|
-
const previousTransaction = transaction.previous
|
|
923
|
-
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
|
+
}
|
|
924
950
|
|
|
925
951
|
if (sessionID) {
|
|
926
|
-
transaction.
|
|
952
|
+
transaction.sourceTxID = {
|
|
927
953
|
sessionID,
|
|
928
954
|
txIndex: meta.mi,
|
|
929
|
-
branch: meta.b ?? previousTransaction?.branch,
|
|
955
|
+
branch: meta.b ?? previousTransaction?.txID.branch,
|
|
930
956
|
};
|
|
931
957
|
} else {
|
|
932
958
|
logger.error("Merge commit without session ID", {
|
|
933
959
|
txID: transaction.txID,
|
|
934
|
-
|
|
960
|
+
prevTxID: previousTransaction?.txID ?? null,
|
|
935
961
|
});
|
|
936
962
|
}
|
|
937
963
|
}
|
|
@@ -952,11 +978,10 @@ export class CoValueCore {
|
|
|
952
978
|
this.determineValidTransactions();
|
|
953
979
|
|
|
954
980
|
for (const transaction of this.verifiedTransactions) {
|
|
955
|
-
|
|
956
|
-
this,
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
);
|
|
981
|
+
if (!ignorePrivateTransactions) {
|
|
982
|
+
decryptTransactionChangesAndMeta(this, transaction);
|
|
983
|
+
}
|
|
984
|
+
|
|
960
985
|
this.parseMetaInformation(transaction);
|
|
961
986
|
}
|
|
962
987
|
}
|
|
@@ -987,7 +1012,7 @@ export class CoValueCore {
|
|
|
987
1012
|
const source = getBranchSource(this);
|
|
988
1013
|
|
|
989
1014
|
for (const transaction of this.verifiedTransactions) {
|
|
990
|
-
if (!isValidTransactionWithChanges(
|
|
1015
|
+
if (!transaction.isValidTransactionWithChanges()) {
|
|
991
1016
|
continue;
|
|
992
1017
|
}
|
|
993
1018
|
|
|
@@ -997,7 +1022,8 @@ export class CoValueCore {
|
|
|
997
1022
|
|
|
998
1023
|
options?.knownTransactions?.add(transaction.tx);
|
|
999
1024
|
|
|
1000
|
-
|
|
1025
|
+
// Using the currentTxID to filter the transactions, because the TxID is modified by the merge meta
|
|
1026
|
+
const txID = transaction.currentTxID;
|
|
1001
1027
|
|
|
1002
1028
|
const from = options?.from?.[txID.sessionID] ?? -1;
|
|
1003
1029
|
|
|
@@ -1121,8 +1147,8 @@ export class CoValueCore {
|
|
|
1121
1147
|
}
|
|
1122
1148
|
|
|
1123
1149
|
compareTransactions(
|
|
1124
|
-
a: Pick<
|
|
1125
|
-
b: Pick<
|
|
1150
|
+
a: Pick<VerifiedTransaction, "madeAt" | "txID">,
|
|
1151
|
+
b: Pick<VerifiedTransaction, "madeAt" | "txID">,
|
|
1126
1152
|
) {
|
|
1127
1153
|
if (a.madeAt !== b.madeAt) {
|
|
1128
1154
|
return a.madeAt - b.madeAt;
|
|
@@ -1195,7 +1221,7 @@ export class CoValueCore {
|
|
|
1195
1221
|
}
|
|
1196
1222
|
}
|
|
1197
1223
|
|
|
1198
|
-
|
|
1224
|
+
safeGetGroup(): RawGroup | undefined {
|
|
1199
1225
|
if (!this.verified) {
|
|
1200
1226
|
throw new Error(
|
|
1201
1227
|
"CoValueCore: getGroup called on coValue without verified state",
|
|
@@ -1203,7 +1229,7 @@ export class CoValueCore {
|
|
|
1203
1229
|
}
|
|
1204
1230
|
|
|
1205
1231
|
if (this.verified.header.ruleset.type !== "ownedByGroup") {
|
|
1206
|
-
|
|
1232
|
+
return undefined;
|
|
1207
1233
|
}
|
|
1208
1234
|
|
|
1209
1235
|
return expectGroup(
|
|
@@ -1213,6 +1239,16 @@ export class CoValueCore {
|
|
|
1213
1239
|
);
|
|
1214
1240
|
}
|
|
1215
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
|
+
|
|
1216
1252
|
getTx(txID: TransactionID): Transaction | undefined {
|
|
1217
1253
|
return this.verified?.sessions.get(txID.sessionID)?.transactions[
|
|
1218
1254
|
txID.txIndex
|
|
@@ -1244,10 +1280,34 @@ export class CoValueCore {
|
|
|
1244
1280
|
return;
|
|
1245
1281
|
}
|
|
1246
1282
|
|
|
1247
|
-
const currentState = this.
|
|
1283
|
+
const currentState = this.getLoadingStateForPeer("storage");
|
|
1248
1284
|
|
|
1249
|
-
if (currentState
|
|
1250
|
-
done
|
|
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");
|
|
1294
|
+
|
|
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");
|
|
1251
1311
|
return;
|
|
1252
1312
|
}
|
|
1253
1313
|
|
|
@@ -1273,7 +1333,7 @@ export class CoValueCore {
|
|
|
1273
1333
|
}
|
|
1274
1334
|
|
|
1275
1335
|
for (const peer of peers) {
|
|
1276
|
-
const currentState = this.
|
|
1336
|
+
const currentState = this.getLoadingStateForPeer(peer.id);
|
|
1277
1337
|
|
|
1278
1338
|
if (currentState === "unknown" || currentState === "unavailable") {
|
|
1279
1339
|
this.markPending(peer.id);
|
|
@@ -1300,40 +1360,34 @@ export class CoValueCore {
|
|
|
1300
1360
|
peer.trackLoadRequestSent(this.id);
|
|
1301
1361
|
}
|
|
1302
1362
|
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
};
|
|
1313
|
-
|
|
1314
|
-
const timeout = setTimeout(markNotFound, CO_VALUE_LOADING_CONFIG.TIMEOUT);
|
|
1315
|
-
const removeCloseListener = peer.persistent
|
|
1316
|
-
? undefined
|
|
1317
|
-
: peer.addCloseListener(markNotFound);
|
|
1318
|
-
|
|
1319
|
-
const listener = (state: CoValueCore) => {
|
|
1320
|
-
const peerState = state.peers.get(peer.id);
|
|
1321
|
-
if (
|
|
1322
|
-
state.isAvailable() || // might have become available from another peer e.g. through handleNewContent
|
|
1323
|
-
peerState?.type === "available" ||
|
|
1324
|
-
peerState?.type === "errored" ||
|
|
1325
|
-
peerState?.type === "unavailable"
|
|
1326
|
-
) {
|
|
1327
|
-
this.listeners.delete(listener);
|
|
1328
|
-
removeCloseListener?.();
|
|
1329
|
-
clearTimeout(timeout);
|
|
1330
|
-
resolve();
|
|
1331
|
-
}
|
|
1332
|
-
};
|
|
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
|
+
};
|
|
1333
1372
|
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
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);
|
|
1337
1391
|
}
|
|
1338
1392
|
}
|
|
1339
1393
|
|
|
@@ -1369,9 +1423,3 @@ export type TryAddTransactionsError =
|
|
|
1369
1423
|
| ResolveAccountAgentError
|
|
1370
1424
|
| InvalidHashError
|
|
1371
1425
|
| InvalidSignatureError;
|
|
1372
|
-
|
|
1373
|
-
function isValidTransactionWithChanges(
|
|
1374
|
-
transaction: VerifiedTransaction,
|
|
1375
|
-
): transaction is VerifiedTransaction & { changes: JsonValue[] } {
|
|
1376
|
-
return Boolean(transaction.isValid && transaction.changes);
|
|
1377
|
-
}
|
|
@@ -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
|
+
}
|