cojson 0.17.11 → 0.17.13
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/coValueCore/SessionMap.d.ts +4 -3
- package/dist/coValueCore/SessionMap.d.ts.map +1 -1
- package/dist/coValueCore/SessionMap.js +15 -4
- package/dist/coValueCore/SessionMap.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +2 -2
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +33 -32
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/utils.d.ts.map +1 -1
- package/dist/coValueCore/utils.js.map +1 -1
- package/dist/coValueCore/verifiedState.d.ts +8 -2
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValueCore/verifiedState.js +7 -4
- package/dist/coValueCore/verifiedState.js.map +1 -1
- package/dist/crypto/PureJSCrypto.d.ts +4 -3
- package/dist/crypto/PureJSCrypto.d.ts.map +1 -1
- package/dist/crypto/PureJSCrypto.js +34 -2
- package/dist/crypto/PureJSCrypto.js.map +1 -1
- package/dist/crypto/WasmCrypto.d.ts +4 -3
- package/dist/crypto/WasmCrypto.d.ts.map +1 -1
- package/dist/crypto/WasmCrypto.js +10 -4
- package/dist/crypto/WasmCrypto.js.map +1 -1
- package/dist/crypto/crypto.d.ts +4 -3
- package/dist/crypto/crypto.d.ts.map +1 -1
- package/dist/localNode.js +1 -1
- package/dist/localNode.js.map +1 -1
- package/dist/queue/LocalTransactionsSyncQueue.d.ts +15 -0
- package/dist/queue/LocalTransactionsSyncQueue.d.ts.map +1 -1
- package/dist/queue/LocalTransactionsSyncQueue.js +25 -0
- package/dist/queue/LocalTransactionsSyncQueue.js.map +1 -1
- package/dist/sync.d.ts +10 -7
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +33 -31
- package/dist/sync.js.map +1 -1
- package/dist/tests/PureJSCrypto.test.js +43 -0
- package/dist/tests/PureJSCrypto.test.js.map +1 -1
- package/dist/tests/WasmCrypto.test.js +55 -0
- package/dist/tests/WasmCrypto.test.js.map +1 -1
- package/dist/tests/coList.test.js +13 -0
- package/dist/tests/coList.test.js.map +1 -1
- package/dist/tests/coMap.test.js +14 -0
- package/dist/tests/coMap.test.js.map +1 -1
- package/dist/tests/coPlainText.test.js +13 -0
- package/dist/tests/coPlainText.test.js.map +1 -1
- package/dist/tests/coStream.test.js +25 -0
- package/dist/tests/coStream.test.js.map +1 -1
- package/dist/tests/coValueCore.test.js +28 -2
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/coreWasm.test.js +1 -1
- package/dist/tests/coreWasm.test.js.map +1 -1
- package/dist/tests/sync.known.test.d.ts +2 -0
- package/dist/tests/sync.known.test.d.ts.map +1 -0
- package/dist/tests/sync.known.test.js +78 -0
- package/dist/tests/sync.known.test.js.map +1 -0
- package/dist/tests/sync.sharding.test.d.ts +2 -0
- package/dist/tests/sync.sharding.test.d.ts.map +1 -0
- package/dist/tests/sync.sharding.test.js +85 -0
- package/dist/tests/sync.sharding.test.js.map +1 -0
- package/dist/tests/sync.storage.test.js +31 -1
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.storageAsync.test.js +1 -1
- package/dist/tests/sync.storageAsync.test.js.map +1 -1
- package/dist/tests/sync.test.js +31 -2
- package/dist/tests/sync.test.js.map +1 -1
- package/dist/tests/sync.upload.test.js +26 -0
- package/dist/tests/sync.upload.test.js.map +1 -1
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +3 -1
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +2 -2
- package/src/coValueCore/SessionMap.ts +24 -1
- package/src/coValueCore/coValueCore.ts +46 -41
- package/src/coValueCore/utils.ts +0 -1
- package/src/coValueCore/verifiedState.ts +14 -3
- package/src/crypto/PureJSCrypto.ts +49 -1
- package/src/crypto/WasmCrypto.ts +15 -1
- package/src/crypto/crypto.ts +7 -1
- package/src/localNode.ts +1 -1
- package/src/queue/LocalTransactionsSyncQueue.ts +32 -1
- package/src/sync.ts +50 -36
- package/src/tests/PureJSCrypto.test.ts +66 -0
- package/src/tests/WasmCrypto.test.ts +88 -0
- package/src/tests/coList.test.ts +19 -0
- package/src/tests/coMap.test.ts +21 -0
- package/src/tests/coPlainText.test.ts +18 -0
- package/src/tests/coStream.test.ts +36 -0
- package/src/tests/coValueCore.test.ts +49 -0
- package/src/tests/coreWasm.test.ts +1 -0
- package/src/tests/sync.known.test.ts +109 -0
- package/src/tests/sync.sharding.test.ts +119 -0
- package/src/tests/sync.storage.test.ts +43 -1
- package/src/tests/sync.storageAsync.test.ts +1 -1
- package/src/tests/sync.test.ts +49 -2
- package/src/tests/sync.upload.test.ts +35 -0
- package/src/tests/testUtils.ts +3 -1
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);
|
|
@@ -163,23 +170,21 @@ export class SyncManager {
|
|
|
163
170
|
this.skipVerify = true;
|
|
164
171
|
}
|
|
165
172
|
|
|
166
|
-
|
|
167
|
-
return
|
|
168
|
-
const aPriority = a.priority || 0;
|
|
169
|
-
const bPriority = b.priority || 0;
|
|
170
|
-
|
|
171
|
-
return bPriority - aPriority;
|
|
172
|
-
});
|
|
173
|
+
getPeers(id: RawCoID): PeerState[] {
|
|
174
|
+
return this.getServerPeers(id).concat(this.getClientPeers());
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
|
|
176
|
-
return Object.values(this.peers);
|
|
177
|
+
getClientPeers(): PeerState[] {
|
|
178
|
+
return Object.values(this.peers).filter((peer) => peer.role === "client");
|
|
177
179
|
}
|
|
178
180
|
|
|
179
|
-
getServerPeers(excludePeerId?: PeerID): PeerState[] {
|
|
180
|
-
|
|
181
|
+
getServerPeers(id: RawCoID, excludePeerId?: PeerID): PeerState[] {
|
|
182
|
+
const serverPeers = Object.values(this.peers).filter(
|
|
181
183
|
(peer) => peer.role === "server" && peer.id !== excludePeerId,
|
|
182
184
|
);
|
|
185
|
+
return this.serverPeerSelector
|
|
186
|
+
? this.serverPeerSelector(id, serverPeers)
|
|
187
|
+
: serverPeers;
|
|
183
188
|
}
|
|
184
189
|
|
|
185
190
|
handleSyncMessage(msg: SyncMessage, peer: PeerState) {
|
|
@@ -221,6 +226,23 @@ export class SyncManager {
|
|
|
221
226
|
id: RawCoID,
|
|
222
227
|
peer: PeerState,
|
|
223
228
|
seen: Set<RawCoID> = new Set(),
|
|
229
|
+
) {
|
|
230
|
+
this.sendNewContent(id, peer, seen, true);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
sendNewContentWithoutDependencies(
|
|
234
|
+
id: RawCoID,
|
|
235
|
+
peer: PeerState,
|
|
236
|
+
seen: Set<RawCoID> = new Set(),
|
|
237
|
+
) {
|
|
238
|
+
this.sendNewContent(id, peer, seen, false);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private sendNewContent(
|
|
242
|
+
id: RawCoID,
|
|
243
|
+
peer: PeerState,
|
|
244
|
+
seen: Set<RawCoID> = new Set(),
|
|
245
|
+
includeDependencies: boolean,
|
|
224
246
|
) {
|
|
225
247
|
if (seen.has(id)) {
|
|
226
248
|
return;
|
|
@@ -234,8 +256,10 @@ export class SyncManager {
|
|
|
234
256
|
return;
|
|
235
257
|
}
|
|
236
258
|
|
|
237
|
-
|
|
238
|
-
|
|
259
|
+
if (includeDependencies) {
|
|
260
|
+
for (const dependency of coValue.getDependedOnCoValues()) {
|
|
261
|
+
this.sendNewContentIncludingDependencies(dependency, peer, seen);
|
|
262
|
+
}
|
|
239
263
|
}
|
|
240
264
|
|
|
241
265
|
const newContentPieces = coValue.verified.newContentSince(
|
|
@@ -402,7 +426,7 @@ export class SyncManager {
|
|
|
402
426
|
return;
|
|
403
427
|
}
|
|
404
428
|
|
|
405
|
-
const peers = this.getServerPeers(peer.id);
|
|
429
|
+
const peers = this.getServerPeers(msg.id, peer.id);
|
|
406
430
|
|
|
407
431
|
coValue.load(peers);
|
|
408
432
|
|
|
@@ -442,7 +466,11 @@ export class SyncManager {
|
|
|
442
466
|
}
|
|
443
467
|
|
|
444
468
|
if (coValue.isAvailable()) {
|
|
445
|
-
|
|
469
|
+
if (peer.role === "server") {
|
|
470
|
+
this.sendNewContentWithoutDependencies(msg.id, peer);
|
|
471
|
+
} else {
|
|
472
|
+
this.sendNewContentIncludingDependencies(msg.id, peer);
|
|
473
|
+
}
|
|
446
474
|
}
|
|
447
475
|
}
|
|
448
476
|
|
|
@@ -524,7 +552,7 @@ export class SyncManager {
|
|
|
524
552
|
if (!dependencyCoValue.hasVerifiedContent()) {
|
|
525
553
|
coValue.markMissingDependency(dependency);
|
|
526
554
|
|
|
527
|
-
const peers = this.getServerPeers();
|
|
555
|
+
const peers = this.getServerPeers(dependencyCoValue.id);
|
|
528
556
|
|
|
529
557
|
// if the peer that sent the content is a client, we add it to the list of peers
|
|
530
558
|
// to also ask them for the dependency
|
|
@@ -607,7 +635,7 @@ export class SyncManager {
|
|
|
607
635
|
// This covers the case where we are getting a new session on an already loaded coValue
|
|
608
636
|
// where we need to load the account to get their public key
|
|
609
637
|
if (!coValue.missingDependencies.has(accountId)) {
|
|
610
|
-
const peers = this.getServerPeers();
|
|
638
|
+
const peers = this.getServerPeers(account.id);
|
|
611
639
|
|
|
612
640
|
if (peer?.role === "client") {
|
|
613
641
|
// if the peer that sent the content is a client, we add it to the list of peers
|
|
@@ -727,7 +755,7 @@ export class SyncManager {
|
|
|
727
755
|
this.storeContent(contentToStore);
|
|
728
756
|
}
|
|
729
757
|
|
|
730
|
-
for (const peer of this.
|
|
758
|
+
for (const peer of this.getPeers(coValue.id)) {
|
|
731
759
|
/**
|
|
732
760
|
* We sync the content against the source peer if it is a client or server peers
|
|
733
761
|
* to upload any content that is available on the current node and not on the source peer.
|
|
@@ -771,26 +799,12 @@ export class SyncManager {
|
|
|
771
799
|
return this.sendNewContentIncludingDependencies(msg.id, peer);
|
|
772
800
|
}
|
|
773
801
|
|
|
774
|
-
dirtyCoValuesTrackingSets: Set<Set<RawCoID>> = new Set();
|
|
775
|
-
trackDirtyCoValues() {
|
|
776
|
-
const trackingSet = new Set<RawCoID>();
|
|
777
|
-
|
|
778
|
-
this.dirtyCoValuesTrackingSets.add(trackingSet);
|
|
779
|
-
|
|
780
|
-
return {
|
|
781
|
-
done: () => {
|
|
782
|
-
this.dirtyCoValuesTrackingSets.delete(trackingSet);
|
|
783
|
-
|
|
784
|
-
return trackingSet;
|
|
785
|
-
},
|
|
786
|
-
};
|
|
787
|
-
}
|
|
788
|
-
|
|
789
802
|
private syncQueue = new LocalTransactionsSyncQueue((content) =>
|
|
790
803
|
this.syncContent(content),
|
|
791
804
|
);
|
|
792
805
|
syncHeader = this.syncQueue.syncHeader;
|
|
793
806
|
syncLocalTransaction = this.syncQueue.syncTransaction;
|
|
807
|
+
trackDirtyCoValues = this.syncQueue.trackDirtyCoValues;
|
|
794
808
|
|
|
795
809
|
syncContent(content: NewContentMessage) {
|
|
796
810
|
const coValue = this.local.getCoValue(content.id);
|
|
@@ -799,7 +813,7 @@ export class SyncManager {
|
|
|
799
813
|
|
|
800
814
|
const contentKnownState = knownStateFromContent(content);
|
|
801
815
|
|
|
802
|
-
for (const peer of this.
|
|
816
|
+
for (const peer of this.getPeers(coValue.id)) {
|
|
803
817
|
if (peer.closed) continue;
|
|
804
818
|
if (coValue.isErroredInPeer(peer.id)) continue;
|
|
805
819
|
|
|
@@ -818,7 +832,7 @@ export class SyncManager {
|
|
|
818
832
|
peer.trackToldKnownState(coValue.id);
|
|
819
833
|
}
|
|
820
834
|
|
|
821
|
-
for (const peer of this.getPeers()) {
|
|
835
|
+
for (const peer of this.getPeers(coValue.id)) {
|
|
822
836
|
this.syncState.triggerUpdate(peer.id, coValue.id);
|
|
823
837
|
}
|
|
824
838
|
}
|
|
@@ -899,7 +913,7 @@ export class SyncManager {
|
|
|
899
913
|
}
|
|
900
914
|
|
|
901
915
|
waitForSync(id: RawCoID, timeout = 60_000) {
|
|
902
|
-
const peers = this.getPeers();
|
|
916
|
+
const peers = this.getPeers(id);
|
|
903
917
|
|
|
904
918
|
return Promise.all(
|
|
905
919
|
peers
|
|
@@ -126,6 +126,72 @@ describe("PureJSCrypto", () => {
|
|
|
126
126
|
|
|
127
127
|
expect(map.get("count")).toEqual(0);
|
|
128
128
|
});
|
|
129
|
+
|
|
130
|
+
it("can add a meta to a private transaction", async () => {
|
|
131
|
+
const client = setupTestNode({
|
|
132
|
+
connected: true,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const group = client.node.createGroup();
|
|
136
|
+
const map = group.createMap();
|
|
137
|
+
|
|
138
|
+
map.core.makeTransaction([], "private", {
|
|
139
|
+
meta: {
|
|
140
|
+
count: 1,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
await map.core.waitForSync();
|
|
145
|
+
|
|
146
|
+
const session2 = client.spawnNewSession();
|
|
147
|
+
|
|
148
|
+
const mapInOtherSession = await loadCoValueOrFail(session2.node, map.id);
|
|
149
|
+
|
|
150
|
+
const decryptedMeta =
|
|
151
|
+
mapInOtherSession.core.verified.decryptTransactionMeta(
|
|
152
|
+
client.node.currentSessionID,
|
|
153
|
+
0,
|
|
154
|
+
map.core.getCurrentReadKey().secret!,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
expect(decryptedMeta).toEqual({
|
|
158
|
+
meta: {
|
|
159
|
+
count: 1,
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("can add a meta to a trusting transaction", async () => {
|
|
165
|
+
const client = setupTestNode({
|
|
166
|
+
connected: true,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const group = client.node.createGroup();
|
|
170
|
+
const map = group.createMap();
|
|
171
|
+
|
|
172
|
+
map.core.makeTransaction([], "trusting", {
|
|
173
|
+
meta: {
|
|
174
|
+
count: 1,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
await map.core.waitForSync();
|
|
179
|
+
|
|
180
|
+
const session2 = client.spawnNewSession();
|
|
181
|
+
|
|
182
|
+
const mapInOtherSession = await loadCoValueOrFail(session2.node, map.id);
|
|
183
|
+
|
|
184
|
+
const transferredMeta = JSON.parse(
|
|
185
|
+
mapInOtherSession.core.verified.sessions.get(client.node.currentSessionID)
|
|
186
|
+
?.transactions[0]?.meta!,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
expect(transferredMeta).toEqual({
|
|
190
|
+
meta: {
|
|
191
|
+
count: 1,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
});
|
|
129
195
|
});
|
|
130
196
|
|
|
131
197
|
describe("PureJSSessionLog", () => {
|
|
@@ -125,4 +125,92 @@ describe("WasmCrypto", () => {
|
|
|
125
125
|
|
|
126
126
|
expect(map.get("count")).toEqual(0);
|
|
127
127
|
});
|
|
128
|
+
|
|
129
|
+
it("can add a meta to a private transaction", async () => {
|
|
130
|
+
const client = setupTestNode({
|
|
131
|
+
connected: true,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const group = client.node.createGroup();
|
|
135
|
+
const map = group.createMap();
|
|
136
|
+
|
|
137
|
+
map.core.makeTransaction([], "private", {
|
|
138
|
+
meta: {
|
|
139
|
+
count: 1,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
await map.core.waitForSync();
|
|
144
|
+
|
|
145
|
+
const session2 = client.spawnNewSession();
|
|
146
|
+
|
|
147
|
+
const mapInOtherSession = await loadCoValueOrFail(session2.node, map.id);
|
|
148
|
+
|
|
149
|
+
const decryptedMeta =
|
|
150
|
+
mapInOtherSession.core.verified.decryptTransactionMeta(
|
|
151
|
+
client.node.currentSessionID,
|
|
152
|
+
0,
|
|
153
|
+
mapInOtherSession.core.getCurrentReadKey().secret!,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
expect(decryptedMeta).toEqual({
|
|
157
|
+
meta: {
|
|
158
|
+
count: 1,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("can add a meta to a trusting transaction", async () => {
|
|
164
|
+
const client = setupTestNode({
|
|
165
|
+
connected: true,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const group = client.node.createGroup();
|
|
169
|
+
const map = group.createMap();
|
|
170
|
+
|
|
171
|
+
map.core.makeTransaction([], "trusting", {
|
|
172
|
+
meta: {
|
|
173
|
+
count: 1,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
await map.core.waitForSync();
|
|
178
|
+
|
|
179
|
+
const session2 = client.spawnNewSession();
|
|
180
|
+
|
|
181
|
+
const mapInOtherSession = await loadCoValueOrFail(session2.node, map.id);
|
|
182
|
+
|
|
183
|
+
const transferredMeta = JSON.parse(
|
|
184
|
+
mapInOtherSession.core.verified.sessions.get(client.node.currentSessionID)
|
|
185
|
+
?.transactions[0]?.meta!,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
expect(transferredMeta).toEqual({
|
|
189
|
+
meta: {
|
|
190
|
+
count: 1,
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("fails to verify signatures without a signer ID", async () => {
|
|
196
|
+
const agentSecret = wasmCrypto.newRandomAgentSecret();
|
|
197
|
+
const sessionID = wasmCrypto.newRandomSessionID(
|
|
198
|
+
wasmCrypto.getAgentID(agentSecret),
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
const sessionLog = wasmCrypto.createSessionLog("co_z12345678", sessionID);
|
|
202
|
+
expect(() =>
|
|
203
|
+
sessionLog.tryAdd(
|
|
204
|
+
[
|
|
205
|
+
{
|
|
206
|
+
privacy: "trusting",
|
|
207
|
+
changes: stableStringify([{ op: "set", key: "count", value: 1 }]),
|
|
208
|
+
madeAt: Date.now(),
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
"signature_z12345678",
|
|
212
|
+
false,
|
|
213
|
+
),
|
|
214
|
+
).toThrow(expect.stringContaining("Signature verification failed"));
|
|
215
|
+
});
|
|
128
216
|
});
|
package/src/tests/coList.test.ts
CHANGED
|
@@ -409,3 +409,22 @@ test("totalValidTransactions should return the number of valid transactions proc
|
|
|
409
409
|
listOnOtherClient.core.getCurrentContent().totalValidTransactions,
|
|
410
410
|
).toEqual(2);
|
|
411
411
|
});
|
|
412
|
+
|
|
413
|
+
test("Should ignore unknown meta transactions", () => {
|
|
414
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
415
|
+
|
|
416
|
+
const coValue = node.createCoValue({
|
|
417
|
+
type: "colist",
|
|
418
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
419
|
+
meta: null,
|
|
420
|
+
...Crypto.createdNowUnique(),
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
coValue.makeTransaction([], "trusting", { unknownMeta: 1 });
|
|
424
|
+
|
|
425
|
+
const content = expectList(coValue.getCurrentContent());
|
|
426
|
+
|
|
427
|
+
content.append("first", 0, "trusting");
|
|
428
|
+
|
|
429
|
+
expect(content.toJSON()).toEqual(["first"]);
|
|
430
|
+
});
|
package/src/tests/coMap.test.ts
CHANGED
|
@@ -217,6 +217,27 @@ test("Can set items in bulk with assign", () => {
|
|
|
217
217
|
});
|
|
218
218
|
});
|
|
219
219
|
|
|
220
|
+
test("Should ignore unknown meta transactions", () => {
|
|
221
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
222
|
+
|
|
223
|
+
const coValue = node.createCoValue({
|
|
224
|
+
type: "comap",
|
|
225
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
226
|
+
meta: null,
|
|
227
|
+
...Crypto.createdNowUnique(),
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
coValue.makeTransaction([], "trusting", { unknownMeta: 1 });
|
|
231
|
+
|
|
232
|
+
const content = expectMap(coValue.getCurrentContent());
|
|
233
|
+
|
|
234
|
+
expect(content.type).toEqual("comap");
|
|
235
|
+
|
|
236
|
+
content.set("key1", "set1", "trusting");
|
|
237
|
+
|
|
238
|
+
expect(content.get("key1")).toEqual("set1");
|
|
239
|
+
});
|
|
240
|
+
|
|
220
241
|
test("totalValidTransactions should return the number of valid transactions processed", async () => {
|
|
221
242
|
const client = setupTestNode({
|
|
222
243
|
connected: true,
|
|
@@ -298,6 +298,24 @@ test("Splits into and from grapheme string arrays", () => {
|
|
|
298
298
|
expect(text).toEqual("👋 안녕!");
|
|
299
299
|
});
|
|
300
300
|
|
|
301
|
+
test("Should ignore unknown meta transactions", () => {
|
|
302
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
303
|
+
|
|
304
|
+
const coValue = node.createCoValue({
|
|
305
|
+
type: "coplaintext",
|
|
306
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
307
|
+
meta: null,
|
|
308
|
+
...Crypto.createdNowUnique(),
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
coValue.makeTransaction([], "trusting", { unknownMeta: 1 });
|
|
312
|
+
|
|
313
|
+
const content = expectPlainText(coValue.getCurrentContent());
|
|
314
|
+
|
|
315
|
+
content.insertAfter(0, "hello", "trusting");
|
|
316
|
+
expect(content.toString()).toEqual("hello");
|
|
317
|
+
});
|
|
318
|
+
|
|
301
319
|
test("chunks transactions when when the chars are longer than MAX_RECOMMENDED_TX_SIZE", async () => {
|
|
302
320
|
setMaxRecommendedTxSize(5);
|
|
303
321
|
|
|
@@ -127,6 +127,42 @@ test("Can push into RawBinaryCoStream", () => {
|
|
|
127
127
|
});
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
+
test("Should ignore meta transactions on RawBinaryCoStream", () => {
|
|
131
|
+
const node = nodeWithRandomAgentAndSessionID();
|
|
132
|
+
|
|
133
|
+
const coValue = node.createCoValue({
|
|
134
|
+
type: "costream",
|
|
135
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
136
|
+
meta: { type: "binary" },
|
|
137
|
+
...Crypto.createdNowUnique(),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
coValue.makeTransaction([], "trusting", { unknownMeta: 1 });
|
|
141
|
+
|
|
142
|
+
const content = coValue.getCurrentContent();
|
|
143
|
+
|
|
144
|
+
if (
|
|
145
|
+
content.type !== "costream" ||
|
|
146
|
+
content.headerMeta?.type !== "binary" ||
|
|
147
|
+
!(content instanceof RawBinaryCoStream)
|
|
148
|
+
) {
|
|
149
|
+
throw new Error("Expected binary stream");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
content.startBinaryStream(
|
|
153
|
+
{ mimeType: "text/plain", fileName: "test.txt" },
|
|
154
|
+
"trusting",
|
|
155
|
+
);
|
|
156
|
+
content.pushBinaryStreamChunk(new Uint8Array([1, 2, 3]), "trusting");
|
|
157
|
+
content.endBinaryStream("trusting");
|
|
158
|
+
expect(content.getBinaryChunks()).toEqual({
|
|
159
|
+
mimeType: "text/plain",
|
|
160
|
+
fileName: "test.txt",
|
|
161
|
+
chunks: [new Uint8Array([1, 2, 3])],
|
|
162
|
+
finished: true,
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
130
166
|
test("When adding large transactions (small fraction of MAX_RECOMMENDED_TX_SIZE), we store an inbetween signature every time we reach MAX_RECOMMENDED_TX_SIZE and split up newContentSince accordingly", () => {
|
|
131
167
|
const node = nodeWithRandomAgentAndSessionID();
|
|
132
168
|
|
|
@@ -20,9 +20,11 @@ import {
|
|
|
20
20
|
loadCoValueOrFail,
|
|
21
21
|
nodeWithRandomAgentAndSessionID,
|
|
22
22
|
randomAgentAndSessionID,
|
|
23
|
+
setupTestNode,
|
|
23
24
|
tearDownTestMetricReader,
|
|
24
25
|
} from "./testUtils.js";
|
|
25
26
|
import { CO_VALUE_PRIORITY } from "../priority.js";
|
|
27
|
+
import { determineValidTransactions } from "../permissions.js";
|
|
26
28
|
|
|
27
29
|
const Crypto = await WasmCrypto.create();
|
|
28
30
|
|
|
@@ -32,6 +34,7 @@ const agentSecret =
|
|
|
32
34
|
|
|
33
35
|
beforeEach(() => {
|
|
34
36
|
metricReader = createTestMetricReader();
|
|
37
|
+
setupTestNode({ isSyncServer: true });
|
|
35
38
|
});
|
|
36
39
|
|
|
37
40
|
afterEach(() => {
|
|
@@ -53,6 +56,7 @@ test("transactions with wrong signature are rejected", () => {
|
|
|
53
56
|
node.currentSessionID,
|
|
54
57
|
node.getCurrentAgent(),
|
|
55
58
|
[{ hello: "world" }],
|
|
59
|
+
undefined,
|
|
56
60
|
);
|
|
57
61
|
|
|
58
62
|
transaction.madeAt = Date.now() + 1000;
|
|
@@ -264,6 +268,51 @@ test("listeners are notified even if the previous listener threw an error", asyn
|
|
|
264
268
|
errorLog.mockRestore();
|
|
265
269
|
});
|
|
266
270
|
|
|
271
|
+
test("creates a transaction with trusting meta information", async () => {
|
|
272
|
+
const client = setupTestNode();
|
|
273
|
+
|
|
274
|
+
const group = client.node.createGroup();
|
|
275
|
+
const map = group.createMap();
|
|
276
|
+
map.core.makeTransaction([], "trusting", {
|
|
277
|
+
meta: true,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const validTransactions = determineValidTransactions(map.core);
|
|
281
|
+
|
|
282
|
+
expect(validTransactions[0]?.tx.meta).toBe(`{"meta":true}`);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("creates a transaction with private meta information", async () => {
|
|
286
|
+
const client = setupTestNode({ connected: true });
|
|
287
|
+
|
|
288
|
+
const group = client.node.createGroup();
|
|
289
|
+
const map = group.createMap();
|
|
290
|
+
map.core.makeTransaction([], "private", {
|
|
291
|
+
meta: true,
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const localTransactionMeta = map.core.verified.decryptTransactionMeta(
|
|
295
|
+
client.node.currentSessionID,
|
|
296
|
+
0,
|
|
297
|
+
map.core.getCurrentReadKey().secret!,
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
expect(localTransactionMeta).toEqual({ meta: true });
|
|
301
|
+
|
|
302
|
+
const newSession = client.spawnNewSession();
|
|
303
|
+
|
|
304
|
+
const mapOnNewSession = await loadCoValueOrFail(newSession.node, map.id);
|
|
305
|
+
|
|
306
|
+
const syncedTransactionMeta =
|
|
307
|
+
mapOnNewSession.core.verified.decryptTransactionMeta(
|
|
308
|
+
client.node.currentSessionID,
|
|
309
|
+
0,
|
|
310
|
+
mapOnNewSession.core.getCurrentReadKey().secret!,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
expect(syncedTransactionMeta).toEqual({ meta: true });
|
|
314
|
+
});
|
|
315
|
+
|
|
267
316
|
test("getValidTransactions should skip private transactions with invalid JSON", () => {
|
|
268
317
|
const [agent, sessionID] = agentAndSessionIDFromSecret(agentSecret);
|
|
269
318
|
const node = new LocalNode(agent.agentSecret, sessionID, Crypto);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test } from "vitest";
|
|
2
|
+
import { setCoValueLoadingRetryDelay } from "../config";
|
|
3
|
+
import {
|
|
4
|
+
SyncMessagesLog,
|
|
5
|
+
TEST_NODE_CONFIG,
|
|
6
|
+
blockMessageTypeOnOutgoingPeer,
|
|
7
|
+
getSyncServerConnectedPeer,
|
|
8
|
+
loadCoValueOrFail,
|
|
9
|
+
setupTestAccount,
|
|
10
|
+
setupTestNode,
|
|
11
|
+
} from "./testUtils";
|
|
12
|
+
|
|
13
|
+
let jazzCloud: ReturnType<typeof setupTestNode>;
|
|
14
|
+
|
|
15
|
+
// Set a short timeout to make the tests on unavailable complete faster
|
|
16
|
+
setCoValueLoadingRetryDelay(100);
|
|
17
|
+
|
|
18
|
+
beforeEach(async () => {
|
|
19
|
+
// We want to simulate a real world communication that happens asynchronously
|
|
20
|
+
TEST_NODE_CONFIG.withAsyncPeers = true;
|
|
21
|
+
|
|
22
|
+
SyncMessagesLog.clear();
|
|
23
|
+
jazzCloud = setupTestNode({ isSyncServer: true });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe("sending known coValues", () => {
|
|
27
|
+
test("dependencies are included when responding to a client", async () => {
|
|
28
|
+
const group = jazzCloud.node.createGroup();
|
|
29
|
+
const map = group.createMap();
|
|
30
|
+
map.set("hello", "world", "trusting");
|
|
31
|
+
|
|
32
|
+
const { node: client } = setupTestNode({
|
|
33
|
+
connected: true,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
map.set("hello2", "world2", "trusting");
|
|
37
|
+
await map.core.waitForSync();
|
|
38
|
+
|
|
39
|
+
const mapOnClient = await loadCoValueOrFail(client, map.id);
|
|
40
|
+
expect(mapOnClient.get("hello")).toEqual("world");
|
|
41
|
+
expect(mapOnClient.get("hello2")).toEqual("world2");
|
|
42
|
+
|
|
43
|
+
expect(
|
|
44
|
+
SyncMessagesLog.getMessages({
|
|
45
|
+
Group: group.core,
|
|
46
|
+
Map: map.core,
|
|
47
|
+
}),
|
|
48
|
+
).toMatchInlineSnapshot(`
|
|
49
|
+
[
|
|
50
|
+
"client -> server | LOAD Map sessions: empty",
|
|
51
|
+
"server -> client | CONTENT Group header: true new: After: 0 New: 3",
|
|
52
|
+
"server -> client | CONTENT Map header: true new: After: 0 New: 2",
|
|
53
|
+
"client -> server | KNOWN Group sessions: header/3",
|
|
54
|
+
"client -> server | KNOWN Map sessions: header/2",
|
|
55
|
+
]
|
|
56
|
+
`);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("dependencies are excluded when responding to a server", async () => {
|
|
60
|
+
// Create a disconnected client
|
|
61
|
+
const { node: client, accountID } = await setupTestAccount({
|
|
62
|
+
connected: false,
|
|
63
|
+
});
|
|
64
|
+
const account = client.expectCurrentAccount(accountID);
|
|
65
|
+
|
|
66
|
+
// Prepare a group -- this will be a non-account dependency of a forthcoming map.
|
|
67
|
+
const group = client.createGroup();
|
|
68
|
+
group.addMember("everyone", "writer");
|
|
69
|
+
|
|
70
|
+
// Let the queue drain
|
|
71
|
+
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
72
|
+
|
|
73
|
+
// Disable transaction verification on the server so it doesn't ask for dependencies.
|
|
74
|
+
jazzCloud.node.syncManager.disableTransactionVerification();
|
|
75
|
+
|
|
76
|
+
// Connect the client, but don't setup syncing just yet...
|
|
77
|
+
const { peer } = getSyncServerConnectedPeer({
|
|
78
|
+
peerId: client.getCurrentAgent().id,
|
|
79
|
+
syncServer: jazzCloud.node,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Disable reconciliation while we setup syncing because we don't want the
|
|
83
|
+
// server to know about our forthcoming map's dependencies (group + account).
|
|
84
|
+
const blocker = blockMessageTypeOnOutgoingPeer(peer, "load", {});
|
|
85
|
+
client.syncManager.addPeer(peer);
|
|
86
|
+
blocker.unblock();
|
|
87
|
+
|
|
88
|
+
// Create a map and set a value on it, this will trigger:
|
|
89
|
+
// - CONTENT from client to server
|
|
90
|
+
// - KNOWN from server to client
|
|
91
|
+
//
|
|
92
|
+
// We don't expect any more messages to be sent from client to server in this
|
|
93
|
+
// case because clients shouldn't greedily send dependencies to a server.
|
|
94
|
+
const map = group.createMap();
|
|
95
|
+
await map.core.waitForSync();
|
|
96
|
+
|
|
97
|
+
const syncMessages = SyncMessagesLog.getMessages({
|
|
98
|
+
Account: account.core,
|
|
99
|
+
Group: group.core,
|
|
100
|
+
Map: map.core,
|
|
101
|
+
});
|
|
102
|
+
expect(
|
|
103
|
+
syncMessages.some(
|
|
104
|
+
(msg) =>
|
|
105
|
+
msg.includes("CONTENT Account") || msg.includes("CONTENT Group"),
|
|
106
|
+
),
|
|
107
|
+
).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
});
|