cojson 0.7.0-alpha.5 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/.eslintrc.cjs +3 -2
  2. package/.prettierrc.js +9 -0
  3. package/.turbo/turbo-build.log +3 -30
  4. package/.turbo/turbo-lint.log +4 -0
  5. package/.turbo/turbo-test.log +1106 -0
  6. package/CHANGELOG.md +104 -0
  7. package/README.md +3 -1
  8. package/dist/base64url.test.js +25 -0
  9. package/dist/base64url.test.js.map +1 -0
  10. package/dist/coValueCore.js +60 -37
  11. package/dist/coValueCore.js.map +1 -1
  12. package/dist/coValues/account.js +16 -15
  13. package/dist/coValues/account.js.map +1 -1
  14. package/dist/coValues/coList.js +1 -1
  15. package/dist/coValues/coList.js.map +1 -1
  16. package/dist/coValues/coMap.js +17 -8
  17. package/dist/coValues/coMap.js.map +1 -1
  18. package/dist/coValues/group.js +13 -14
  19. package/dist/coValues/group.js.map +1 -1
  20. package/dist/coreToCoValue.js.map +1 -1
  21. package/dist/crypto/PureJSCrypto.js +89 -0
  22. package/dist/crypto/PureJSCrypto.js.map +1 -0
  23. package/dist/crypto/WasmCrypto.js +127 -0
  24. package/dist/crypto/WasmCrypto.js.map +1 -0
  25. package/dist/crypto/crypto.js +151 -0
  26. package/dist/crypto/crypto.js.map +1 -0
  27. package/dist/ids.js +4 -2
  28. package/dist/ids.js.map +1 -1
  29. package/dist/index.js +6 -8
  30. package/dist/index.js.map +1 -1
  31. package/dist/jsonStringify.js.map +1 -1
  32. package/dist/localNode.js +41 -38
  33. package/dist/localNode.js.map +1 -1
  34. package/dist/permissions.js +6 -6
  35. package/dist/permissions.js.map +1 -1
  36. package/dist/storage/FileSystem.js +61 -0
  37. package/dist/storage/FileSystem.js.map +1 -0
  38. package/dist/storage/chunksAndKnownStates.js +97 -0
  39. package/dist/storage/chunksAndKnownStates.js.map +1 -0
  40. package/dist/storage/index.js +265 -0
  41. package/dist/storage/index.js.map +1 -0
  42. package/dist/sync.js +29 -25
  43. package/dist/sync.js.map +1 -1
  44. package/dist/tests/account.test.js +58 -0
  45. package/dist/tests/account.test.js.map +1 -0
  46. package/dist/tests/coList.test.js +76 -0
  47. package/dist/tests/coList.test.js.map +1 -0
  48. package/dist/tests/coMap.test.js +136 -0
  49. package/dist/tests/coMap.test.js.map +1 -0
  50. package/dist/tests/coStream.test.js +172 -0
  51. package/dist/tests/coStream.test.js.map +1 -0
  52. package/dist/tests/coValueCore.test.js +114 -0
  53. package/dist/tests/coValueCore.test.js.map +1 -0
  54. package/dist/tests/crypto.test.js +118 -0
  55. package/dist/tests/crypto.test.js.map +1 -0
  56. package/dist/tests/cryptoImpl.test.js +113 -0
  57. package/dist/tests/cryptoImpl.test.js.map +1 -0
  58. package/dist/tests/group.test.js +34 -0
  59. package/dist/tests/group.test.js.map +1 -0
  60. package/dist/tests/permissions.test.js +1060 -0
  61. package/dist/tests/permissions.test.js.map +1 -0
  62. package/dist/tests/sync.test.js +816 -0
  63. package/dist/tests/sync.test.js.map +1 -0
  64. package/dist/tests/testUtils.js +12 -11
  65. package/dist/tests/testUtils.js.map +1 -1
  66. package/dist/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
  67. package/dist/typeUtils/isAccountID.js.map +1 -1
  68. package/dist/typeUtils/isCoValue.js.map +1 -1
  69. package/package.json +14 -27
  70. package/src/base64url.test.ts +6 -5
  71. package/src/coValue.ts +1 -1
  72. package/src/coValueCore.ts +179 -126
  73. package/src/coValues/account.ts +30 -32
  74. package/src/coValues/coList.ts +11 -11
  75. package/src/coValues/coMap.ts +27 -17
  76. package/src/coValues/coStream.ts +17 -17
  77. package/src/coValues/group.ts +93 -109
  78. package/src/coreToCoValue.ts +5 -2
  79. package/src/crypto/PureJSCrypto.ts +200 -0
  80. package/src/crypto/WasmCrypto.ts +259 -0
  81. package/src/crypto/crypto.ts +336 -0
  82. package/src/ids.ts +8 -7
  83. package/src/index.ts +24 -24
  84. package/src/jsonStringify.ts +6 -4
  85. package/src/jsonValue.ts +2 -2
  86. package/src/localNode.ts +103 -109
  87. package/src/media.ts +3 -3
  88. package/src/permissions.ts +19 -21
  89. package/src/storage/FileSystem.ts +152 -0
  90. package/src/storage/chunksAndKnownStates.ts +139 -0
  91. package/src/storage/index.ts +479 -0
  92. package/src/streamUtils.ts +12 -12
  93. package/src/sync.ts +79 -63
  94. package/src/tests/account.test.ts +15 -15
  95. package/src/tests/coList.test.ts +94 -0
  96. package/src/tests/coMap.test.ts +162 -0
  97. package/src/tests/coStream.test.ts +246 -0
  98. package/src/tests/coValueCore.test.ts +36 -37
  99. package/src/tests/crypto.test.ts +66 -72
  100. package/src/tests/cryptoImpl.test.ts +183 -0
  101. package/src/tests/group.test.ts +16 -17
  102. package/src/tests/permissions.test.ts +269 -283
  103. package/src/tests/sync.test.ts +122 -123
  104. package/src/tests/testUtils.ts +24 -21
  105. package/src/typeUtils/accountOrAgentIDfromSessionID.ts +1 -2
  106. package/src/typeUtils/expectGroup.ts +1 -1
  107. package/src/typeUtils/isAccountID.ts +0 -1
  108. package/src/typeUtils/isCoValue.ts +1 -2
  109. package/tsconfig.json +0 -1
  110. package/dist/crypto.js +0 -254
  111. package/dist/crypto.js.map +0 -1
  112. package/src/crypto.ts +0 -484
  113. package/src/tests/coValue.test.ts +0 -497
@@ -0,0 +1,246 @@
1
+ import { expect, test } from "vitest";
2
+ import { expectStream } from "../coValue.js";
3
+ import { RawBinaryCoStream } from "../coValues/coStream.js";
4
+ import { MAX_RECOMMENDED_TX_SIZE, WasmCrypto } from "../index.js";
5
+ import { LocalNode } from "../localNode.js";
6
+ import { randomAnonymousAccountAndSessionID } from "./testUtils.js";
7
+
8
+ const Crypto = await WasmCrypto.create();
9
+
10
+ test("Empty CoStream works", () => {
11
+ const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
12
+
13
+ const coValue = node.createCoValue({
14
+ type: "costream",
15
+ ruleset: { type: "unsafeAllowAll" },
16
+ meta: null,
17
+ ...Crypto.createdNowUnique(),
18
+ });
19
+
20
+ const content = expectStream(coValue.getCurrentContent());
21
+
22
+ expect(content.type).toEqual("costream");
23
+ expect(content.toJSON()).toEqual({});
24
+ expect(content.getSingleStream()).toEqual(undefined);
25
+ });
26
+
27
+ test("Can push into CoStream", () => {
28
+ const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
29
+
30
+ const coValue = node.createCoValue({
31
+ type: "costream",
32
+ ruleset: { type: "unsafeAllowAll" },
33
+ meta: null,
34
+ ...Crypto.createdNowUnique(),
35
+ });
36
+
37
+ const content = expectStream(coValue.getCurrentContent());
38
+
39
+ content.push({ hello: "world" }, "trusting");
40
+ expect(content.toJSON()).toEqual({
41
+ [node.currentSessionID]: [{ hello: "world" }],
42
+ });
43
+ content.push({ foo: "bar" }, "trusting");
44
+ expect(content.toJSON()).toEqual({
45
+ [node.currentSessionID]: [{ hello: "world" }, { foo: "bar" }],
46
+ });
47
+ expect(content.getSingleStream()).toEqual([
48
+ { hello: "world" },
49
+ { foo: "bar" },
50
+ ]);
51
+ });
52
+
53
+ test("Empty RawBinaryCoStream works", () => {
54
+ const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
55
+
56
+ const coValue = node.createCoValue({
57
+ type: "costream",
58
+ ruleset: { type: "unsafeAllowAll" },
59
+ meta: { type: "binary" },
60
+ ...Crypto.createdNowUnique(),
61
+ });
62
+
63
+ const content = coValue.getCurrentContent();
64
+
65
+ if (
66
+ content.type !== "costream" ||
67
+ content.headerMeta?.type !== "binary" ||
68
+ !(content instanceof RawBinaryCoStream)
69
+ ) {
70
+ throw new Error("Expected binary stream");
71
+ }
72
+
73
+ expect(content.type).toEqual("costream");
74
+ expect(content.headerMeta.type).toEqual("binary");
75
+ expect(content.toJSON()).toEqual({});
76
+ expect(content.getBinaryChunks()).toEqual(undefined);
77
+ });
78
+
79
+ test("Can push into RawBinaryCoStream", () => {
80
+ const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
81
+
82
+ const coValue = node.createCoValue({
83
+ type: "costream",
84
+ ruleset: { type: "unsafeAllowAll" },
85
+ meta: { type: "binary" },
86
+ ...Crypto.createdNowUnique(),
87
+ });
88
+
89
+ const content = coValue.getCurrentContent();
90
+
91
+ if (
92
+ content.type !== "costream" ||
93
+ content.headerMeta?.type !== "binary" ||
94
+ !(content instanceof RawBinaryCoStream)
95
+ ) {
96
+ throw new Error("Expected binary stream");
97
+ }
98
+
99
+ content.startBinaryStream(
100
+ { mimeType: "text/plain", fileName: "test.txt" },
101
+ "trusting",
102
+ );
103
+ content.pushBinaryStreamChunk(new Uint8Array([1, 2, 3]), "trusting");
104
+ content.pushBinaryStreamChunk(new Uint8Array([4, 5, 6]), "trusting");
105
+
106
+ content.endBinaryStream("trusting");
107
+ expect(content.getBinaryChunks()).toEqual({
108
+ mimeType: "text/plain",
109
+ fileName: "test.txt",
110
+ chunks: [new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])],
111
+ finished: true,
112
+ });
113
+ });
114
+
115
+ 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", () => {
116
+ const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
117
+
118
+ const coValue = node.createCoValue({
119
+ type: "costream",
120
+ ruleset: { type: "unsafeAllowAll" },
121
+ meta: { type: "binary" },
122
+ ...Crypto.createdNowUnique(),
123
+ });
124
+
125
+ const content = coValue.getCurrentContent();
126
+
127
+ if (
128
+ content.type !== "costream" ||
129
+ content.headerMeta?.type !== "binary" ||
130
+ !(content instanceof RawBinaryCoStream)
131
+ ) {
132
+ throw new Error("Expected binary stream");
133
+ }
134
+
135
+ content.startBinaryStream(
136
+ { mimeType: "text/plain", fileName: "test.txt" },
137
+ "trusting",
138
+ );
139
+
140
+ for (let i = 0; i < 10; i++) {
141
+ const chunk = new Uint8Array(MAX_RECOMMENDED_TX_SIZE / 3 + 100);
142
+
143
+ content.pushBinaryStreamChunk(chunk, "trusting");
144
+ }
145
+
146
+ content.endBinaryStream("trusting");
147
+
148
+ const sessionEntry = coValue.sessionLogs.get(node.currentSessionID)!;
149
+ expect(sessionEntry.transactions.length).toEqual(12);
150
+ expect(sessionEntry.signatureAfter[0]).not.toBeDefined();
151
+ expect(sessionEntry.signatureAfter[1]).not.toBeDefined();
152
+ expect(sessionEntry.signatureAfter[2]).not.toBeDefined();
153
+ expect(sessionEntry.signatureAfter[3]).toBeDefined();
154
+ expect(sessionEntry.signatureAfter[4]).not.toBeDefined();
155
+ expect(sessionEntry.signatureAfter[5]).not.toBeDefined();
156
+ expect(sessionEntry.signatureAfter[6]).toBeDefined();
157
+ expect(sessionEntry.signatureAfter[7]).not.toBeDefined();
158
+ expect(sessionEntry.signatureAfter[8]).not.toBeDefined();
159
+ expect(sessionEntry.signatureAfter[9]).toBeDefined();
160
+ expect(sessionEntry.signatureAfter[10]).not.toBeDefined();
161
+ expect(sessionEntry.signatureAfter[11]).not.toBeDefined();
162
+
163
+ const newContent = coValue.newContentSince({
164
+ id: coValue.id,
165
+ header: false,
166
+ sessions: {},
167
+ })!;
168
+
169
+ expect(newContent.length).toEqual(5);
170
+ expect(newContent[0]!.header).toBeDefined();
171
+ expect(newContent[1]!.new[node.currentSessionID]!.lastSignature).toEqual(
172
+ sessionEntry.signatureAfter[3],
173
+ );
174
+ expect(newContent[2]!.new[node.currentSessionID]!.lastSignature).toEqual(
175
+ sessionEntry.signatureAfter[6],
176
+ );
177
+ expect(newContent[3]!.new[node.currentSessionID]!.lastSignature).toEqual(
178
+ sessionEntry.signatureAfter[9],
179
+ );
180
+ expect(newContent[4]!.new[node.currentSessionID]!.lastSignature).toEqual(
181
+ sessionEntry.lastSignature,
182
+ );
183
+ });
184
+
185
+ test("When adding large transactions (bigger than MAX_RECOMMENDED_TX_SIZE), we store an inbetween signature after every large transaction and split up newContentSince accordingly", () => {
186
+ const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
187
+
188
+ const coValue = node.createCoValue({
189
+ type: "costream",
190
+ ruleset: { type: "unsafeAllowAll" },
191
+ meta: { type: "binary" },
192
+ ...Crypto.createdNowUnique(),
193
+ });
194
+
195
+ const content = coValue.getCurrentContent();
196
+
197
+ if (
198
+ content.type !== "costream" ||
199
+ content.headerMeta?.type !== "binary" ||
200
+ !(content instanceof RawBinaryCoStream)
201
+ ) {
202
+ throw new Error("Expected binary stream");
203
+ }
204
+
205
+ content.startBinaryStream(
206
+ { mimeType: "text/plain", fileName: "test.txt" },
207
+ "trusting",
208
+ );
209
+
210
+ const chunk = new Uint8Array(MAX_RECOMMENDED_TX_SIZE + 100);
211
+
212
+ for (let i = 0; i < 3; i++) {
213
+ content.pushBinaryStreamChunk(chunk, "trusting");
214
+ }
215
+
216
+ content.endBinaryStream("trusting");
217
+
218
+ const sessionEntry = coValue.sessionLogs.get(node.currentSessionID)!;
219
+ expect(sessionEntry.transactions.length).toEqual(5);
220
+ expect(sessionEntry.signatureAfter[0]).not.toBeDefined();
221
+ expect(sessionEntry.signatureAfter[1]).toBeDefined();
222
+ expect(sessionEntry.signatureAfter[2]).toBeDefined();
223
+ expect(sessionEntry.signatureAfter[3]).toBeDefined();
224
+ expect(sessionEntry.signatureAfter[4]).not.toBeDefined();
225
+
226
+ const newContent = coValue.newContentSince({
227
+ id: coValue.id,
228
+ header: false,
229
+ sessions: {},
230
+ })!;
231
+
232
+ expect(newContent.length).toEqual(5);
233
+ expect(newContent[0]!.header).toBeDefined();
234
+ expect(newContent[1]!.new[node.currentSessionID]!.lastSignature).toEqual(
235
+ sessionEntry.signatureAfter[1],
236
+ );
237
+ expect(newContent[2]!.new[node.currentSessionID]!.lastSignature).toEqual(
238
+ sessionEntry.signatureAfter[2],
239
+ );
240
+ expect(newContent[3]!.new[node.currentSessionID]!.lastSignature).toEqual(
241
+ sessionEntry.signatureAfter[3],
242
+ );
243
+ expect(newContent[4]!.new[node.currentSessionID]!.lastSignature).toEqual(
244
+ sessionEntry.lastSignature,
245
+ );
246
+ });
@@ -1,32 +1,23 @@
1
- import { expect, test, beforeEach } from "vitest";
1
+ import { expect, test, vi } from "vitest";
2
2
  import { Transaction } from "../coValueCore.js";
3
3
  import { LocalNode } from "../localNode.js";
4
- import { createdNowUnique, getAgentSignerSecret, newRandomAgentSecret, sign } from "../crypto.js";
5
4
  import { randomAnonymousAccountAndSessionID } from "./testUtils.js";
6
5
  import { MapOpPayload } from "../coValues/coMap.js";
7
6
  import { Role } from "../permissions.js";
8
- import { cojsonReady } from "../index.js";
9
7
  import { stableStringify } from "../jsonStringify.js";
8
+ import { WasmCrypto } from "../crypto/WasmCrypto.js";
10
9
 
11
- import { webcrypto } from "node:crypto";
12
- if (!("crypto" in globalThis)) {
13
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
- (globalThis as any).crypto = webcrypto;
15
- }
16
-
17
- beforeEach(async () => {
18
- await cojsonReady;
19
- });
10
+ const Crypto = await WasmCrypto.create();
20
11
 
21
12
  test("Can create coValue with new agent credentials and add transaction to it", () => {
22
13
  const [account, sessionID] = randomAnonymousAccountAndSessionID();
23
- const node = new LocalNode(account, sessionID);
14
+ const node = new LocalNode(account, sessionID, Crypto);
24
15
 
25
16
  const coValue = node.createCoValue({
26
17
  type: "costream",
27
18
  ruleset: { type: "unsafeAllowAll" },
28
19
  meta: null,
29
- ...createdNowUnique(),
20
+ ...Crypto.createdNowUnique(),
30
21
  });
31
22
 
32
23
  const transaction: Transaction = {
@@ -41,7 +32,7 @@ test("Can create coValue with new agent credentials and add transaction to it",
41
32
 
42
33
  const { expectedNewHash } = coValue.expectedNewHashAfter(
43
34
  node.currentSessionID,
44
- [transaction]
35
+ [transaction],
45
36
  );
46
37
 
47
38
  expect(
@@ -49,21 +40,21 @@ test("Can create coValue with new agent credentials and add transaction to it",
49
40
  node.currentSessionID,
50
41
  [transaction],
51
42
  expectedNewHash,
52
- sign(account.currentSignerSecret(), expectedNewHash)
53
- )
43
+ Crypto.sign(account.currentSignerSecret(), expectedNewHash),
44
+ ),
54
45
  ).toBe(true);
55
46
  });
56
47
 
57
48
  test("transactions with wrong signature are rejected", () => {
58
- const wrongAgent = newRandomAgentSecret();
49
+ const wrongAgent = Crypto.newRandomAgentSecret();
59
50
  const [agentSecret, sessionID] = randomAnonymousAccountAndSessionID();
60
- const node = new LocalNode(agentSecret, sessionID);
51
+ const node = new LocalNode(agentSecret, sessionID, Crypto);
61
52
 
62
53
  const coValue = node.createCoValue({
63
54
  type: "costream",
64
55
  ruleset: { type: "unsafeAllowAll" },
65
56
  meta: null,
66
- ...createdNowUnique(),
57
+ ...Crypto.createdNowUnique(),
67
58
  });
68
59
 
69
60
  const transaction: Transaction = {
@@ -78,7 +69,7 @@ test("transactions with wrong signature are rejected", () => {
78
69
 
79
70
  const { expectedNewHash } = coValue.expectedNewHashAfter(
80
71
  node.currentSessionID,
81
- [transaction]
72
+ [transaction],
82
73
  );
83
74
 
84
75
  expect(
@@ -86,20 +77,23 @@ test("transactions with wrong signature are rejected", () => {
86
77
  node.currentSessionID,
87
78
  [transaction],
88
79
  expectedNewHash,
89
- sign(getAgentSignerSecret(wrongAgent), expectedNewHash)
90
- )
80
+ Crypto.sign(
81
+ Crypto.getAgentSignerSecret(wrongAgent),
82
+ expectedNewHash,
83
+ ),
84
+ ),
91
85
  ).toBe(false);
92
86
  });
93
87
 
94
88
  test("transactions with correctly signed, but wrong hash are rejected", () => {
95
89
  const [account, sessionID] = randomAnonymousAccountAndSessionID();
96
- const node = new LocalNode(account, sessionID);
90
+ const node = new LocalNode(account, sessionID, Crypto);
97
91
 
98
92
  const coValue = node.createCoValue({
99
93
  type: "costream",
100
94
  ruleset: { type: "unsafeAllowAll" },
101
95
  meta: null,
102
- ...createdNowUnique(),
96
+ ...Crypto.createdNowUnique(),
103
97
  });
104
98
 
105
99
  const transaction: Transaction = {
@@ -124,7 +118,7 @@ test("transactions with correctly signed, but wrong hash are rejected", () => {
124
118
  },
125
119
  ]),
126
120
  },
127
- ]
121
+ ],
128
122
  );
129
123
 
130
124
  expect(
@@ -132,14 +126,14 @@ test("transactions with correctly signed, but wrong hash are rejected", () => {
132
126
  node.currentSessionID,
133
127
  [transaction],
134
128
  expectedNewHash,
135
- sign(account.currentSignerSecret(), expectedNewHash)
136
- )
129
+ Crypto.sign(account.currentSignerSecret(), expectedNewHash),
130
+ ),
137
131
  ).toBe(false);
138
132
  });
139
133
 
140
134
  test("New transactions in a group correctly update owned values, including subscriptions", async () => {
141
135
  const [account, sessionID] = randomAnonymousAccountAndSessionID();
142
- const node = new LocalNode(account, sessionID);
136
+ const node = new LocalNode(account, sessionID, Crypto);
143
137
 
144
138
  const group = node.createGroup();
145
139
 
@@ -147,11 +141,11 @@ test("New transactions in a group correctly update owned values, including subsc
147
141
 
148
142
  await new Promise((resolve) => setTimeout(resolve, 10));
149
143
 
150
- let map = group.createMap();
144
+ const map = group.createMap();
151
145
 
152
146
  map.set("hello", "world");
153
147
 
154
- const listener = jest.fn().mockImplementation();
148
+ const listener = vi.fn();
155
149
 
156
150
  map.subscribe(listener);
157
151
 
@@ -164,23 +158,28 @@ test("New transactions in a group correctly update owned values, including subsc
164
158
  {
165
159
  op: "set",
166
160
  key: account.id,
167
- value: "revoked"
168
- } satisfies MapOpPayload<typeof account.id, Role>
169
- ])
161
+ value: "revoked",
162
+ } satisfies MapOpPayload<typeof account.id, Role>,
163
+ ]),
170
164
  } satisfies Transaction;
171
165
 
172
166
  const { expectedNewHash } = group.core.expectedNewHashAfter(sessionID, [
173
167
  resignationThatWeJustLearnedAbout,
174
168
  ]);
175
169
 
176
- const signature = sign(
170
+ const signature = Crypto.sign(
177
171
  node.account.currentSignerSecret(),
178
- expectedNewHash
172
+ expectedNewHash,
179
173
  );
180
174
 
181
175
  expect(map.core.getValidSortedTransactions().length).toBe(1);
182
176
 
183
- const manuallyAdddedTxSuccess = group.core.tryAddTransactions(node.currentSessionID, [resignationThatWeJustLearnedAbout], expectedNewHash, signature);
177
+ const manuallyAdddedTxSuccess = group.core.tryAddTransactions(
178
+ node.currentSessionID,
179
+ [resignationThatWeJustLearnedAbout],
180
+ expectedNewHash,
181
+ signature,
182
+ );
184
183
 
185
184
  expect(manuallyAdddedTxSuccess).toBe(true);
186
185
 
@@ -1,93 +1,84 @@
1
- import { expect, test, beforeEach } from "vitest";
2
- import {
3
- getSealerID,
4
- getSignerID,
5
- secureHash,
6
- newRandomSealer,
7
- newRandomSigner,
8
- seal,
9
- sign,
10
- unseal,
11
- verify,
12
- shortHash,
13
- newRandomKeySecret,
14
- encryptForTransaction,
15
- decryptForTransaction,
16
- encryptKeySecret,
17
- decryptKeySecret,
18
- } from "../crypto.js";
1
+ import { expect, test } from "vitest";
2
+ import { WasmCrypto } from "../crypto/WasmCrypto.js";
19
3
  import { base58, base64url } from "@scure/base";
20
4
  import { x25519 } from "@noble/curves/ed25519";
21
5
  import { xsalsa20_poly1305 } from "@noble/ciphers/salsa";
22
6
  import { blake3 } from "@noble/hashes/blake3";
23
- import stableStringify from "fast-json-stable-stringify";
24
7
  import { SessionID } from "../ids.js";
25
- import { cojsonReady } from "../index.js";
8
+ import { stableStringify } from "../jsonStringify.js";
26
9
 
27
- import { webcrypto } from "node:crypto";
28
- if (!("crypto" in globalThis)) {
29
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
- (globalThis as any).crypto = webcrypto;
31
- }
32
-
33
- beforeEach(async () => {
34
- await cojsonReady;
35
- });
10
+ const Crypto = await WasmCrypto.create();
36
11
 
37
12
  test("Signatures round-trip and use stable stringify", () => {
38
13
  const data = { b: "world", a: "hello" };
39
- const signer = newRandomSigner();
40
- const signature = sign(signer, data);
14
+ const signer = Crypto.newRandomSigner();
15
+ const signature = Crypto.sign(signer, data);
41
16
 
42
17
  expect(signature).toMatch(/^signature_z/);
43
18
  expect(
44
- verify(signature, { a: "hello", b: "world" }, getSignerID(signer))
19
+ Crypto.verify(
20
+ signature,
21
+ { a: "hello", b: "world" },
22
+ Crypto.getSignerID(signer),
23
+ ),
45
24
  ).toBe(true);
46
25
  });
47
26
 
48
27
  test("Invalid signatures don't verify", () => {
49
28
  const data = { b: "world", a: "hello" };
50
- const signer = newRandomSigner();
51
- const signer2 = newRandomSigner();
52
- const wrongSignature = sign(signer2, data);
29
+ const signer = Crypto.newRandomSigner();
30
+ const signer2 = Crypto.newRandomSigner();
31
+ const wrongSignature = Crypto.sign(signer2, data);
53
32
 
54
- expect(verify(wrongSignature, data, getSignerID(signer))).toBe(false);
33
+ expect(
34
+ Crypto.verify(wrongSignature, data, Crypto.getSignerID(signer)),
35
+ ).toBe(false);
55
36
  });
56
37
 
57
38
  test("encrypting round-trips, but invalid receiver can't unseal", () => {
58
39
  const data = { b: "world", a: "hello" };
59
- const sender = newRandomSealer();
60
- const sealer = newRandomSealer();
61
- const wrongSealer = newRandomSealer();
40
+ const sender = Crypto.newRandomSealer();
41
+ const sealer = Crypto.newRandomSealer();
42
+ const wrongSealer = Crypto.newRandomSealer();
62
43
 
63
44
  const nOnceMaterial = {
64
45
  in: "co_zTEST",
65
46
  tx: { sessionID: "co_zTEST_session_zTEST" as SessionID, txIndex: 0 },
66
47
  } as const;
67
48
 
68
- const sealed = seal({
49
+ const sealed = Crypto.seal({
69
50
  message: data,
70
51
  from: sender,
71
- to: getSealerID(sealer),
52
+ to: Crypto.getSealerID(sealer),
72
53
  nOnceMaterial,
73
54
  });
74
55
 
75
- expect(unseal(sealed, sealer, getSealerID(sender), nOnceMaterial)).toEqual(
76
- data
77
- );
56
+ expect(
57
+ Crypto.unseal(
58
+ sealed,
59
+ sealer,
60
+ Crypto.getSealerID(sender),
61
+ nOnceMaterial,
62
+ ),
63
+ ).toEqual(data);
78
64
  expect(() =>
79
- unseal(sealed, wrongSealer, getSealerID(sender), nOnceMaterial)
65
+ Crypto.unseal(
66
+ sealed,
67
+ wrongSealer,
68
+ Crypto.getSealerID(sender),
69
+ nOnceMaterial,
70
+ ),
80
71
  ).toThrow(/Wrong tag/);
81
72
 
82
73
  // trying with wrong sealer secret, by hand
83
74
  const nOnce = blake3(
84
- new TextEncoder().encode(stableStringify(nOnceMaterial))
75
+ new TextEncoder().encode(stableStringify(nOnceMaterial)),
85
76
  ).slice(0, 24);
86
77
  const sealer3priv = base58.decode(
87
- wrongSealer.substring("sealerSecret_z".length)
78
+ wrongSealer.substring("sealerSecret_z".length),
88
79
  );
89
80
  const senderPub = base58.decode(
90
- getSealerID(sender).substring("sealer_z".length)
81
+ Crypto.getSealerID(sender).substring("sealer_z".length),
91
82
  );
92
83
  const sealedBytes = base64url.decode(sealed.substring("sealed_U".length));
93
84
  const sharedSecret = x25519.getSharedSecret(sealer3priv, senderPub);
@@ -98,34 +89,34 @@ test("encrypting round-trips, but invalid receiver can't unseal", () => {
98
89
  });
99
90
 
100
91
  test("Hashing is deterministic", () => {
101
- expect(secureHash({ b: "world", a: "hello" })).toEqual(
102
- secureHash({ a: "hello", b: "world" })
92
+ expect(Crypto.secureHash({ b: "world", a: "hello" })).toEqual(
93
+ Crypto.secureHash({ a: "hello", b: "world" }),
103
94
  );
104
95
 
105
- expect(shortHash({ b: "world", a: "hello" })).toEqual(
106
- shortHash({ a: "hello", b: "world" })
96
+ expect(Crypto.shortHash({ b: "world", a: "hello" })).toEqual(
97
+ Crypto.shortHash({ a: "hello", b: "world" }),
107
98
  );
108
99
  });
109
100
 
110
101
  test("Encryption for transactions round-trips", () => {
111
- const { secret } = newRandomKeySecret();
102
+ const { secret } = Crypto.newRandomKeySecret();
112
103
 
113
- const encrypted1 = encryptForTransaction({ a: "hello" }, secret, {
104
+ const encrypted1 = Crypto.encryptForTransaction({ a: "hello" }, secret, {
114
105
  in: "co_zTEST",
115
106
  tx: { sessionID: "co_zTEST_session_zTEST" as SessionID, txIndex: 0 },
116
107
  });
117
108
 
118
- const encrypted2 = encryptForTransaction({ b: "world" }, secret, {
109
+ const encrypted2 = Crypto.encryptForTransaction({ b: "world" }, secret, {
119
110
  in: "co_zTEST",
120
111
  tx: { sessionID: "co_zTEST_session_zTEST" as SessionID, txIndex: 1 },
121
112
  });
122
113
 
123
- const decrypted1 = decryptForTransaction(encrypted1, secret, {
114
+ const decrypted1 = Crypto.decryptForTransaction(encrypted1, secret, {
124
115
  in: "co_zTEST",
125
116
  tx: { sessionID: "co_zTEST_session_zTEST" as SessionID, txIndex: 0 },
126
117
  });
127
118
 
128
- const decrypted2 = decryptForTransaction(encrypted2, secret, {
119
+ const decrypted2 = Crypto.decryptForTransaction(encrypted2, secret, {
129
120
  in: "co_zTEST",
130
121
  tx: { sessionID: "co_zTEST_session_zTEST" as SessionID, txIndex: 1 },
131
122
  });
@@ -134,25 +125,25 @@ test("Encryption for transactions round-trips", () => {
134
125
  });
135
126
 
136
127
  test("Encryption for transactions doesn't decrypt with a wrong key", () => {
137
- const { secret } = newRandomKeySecret();
138
- const { secret: secret2 } = newRandomKeySecret();
128
+ const { secret } = Crypto.newRandomKeySecret();
129
+ const { secret: secret2 } = Crypto.newRandomKeySecret();
139
130
 
140
- const encrypted1 = encryptForTransaction({ a: "hello" }, secret, {
131
+ const encrypted1 = Crypto.encryptForTransaction({ a: "hello" }, secret, {
141
132
  in: "co_zTEST",
142
133
  tx: { sessionID: "co_zTEST_session_zTEST" as SessionID, txIndex: 0 },
143
134
  });
144
135
 
145
- const encrypted2 = encryptForTransaction({ b: "world" }, secret, {
136
+ const encrypted2 = Crypto.encryptForTransaction({ b: "world" }, secret, {
146
137
  in: "co_zTEST",
147
138
  tx: { sessionID: "co_zTEST_session_zTEST" as SessionID, txIndex: 1 },
148
139
  });
149
140
 
150
- const decrypted1 = decryptForTransaction(encrypted1, secret2, {
141
+ const decrypted1 = Crypto.decryptForTransaction(encrypted1, secret2, {
151
142
  in: "co_zTEST",
152
143
  tx: { sessionID: "co_zTEST_session_zTEST" as SessionID, txIndex: 0 },
153
144
  });
154
145
 
155
- const decrypted2 = decryptForTransaction(encrypted2, secret2, {
146
+ const decrypted2 = Crypto.decryptForTransaction(encrypted2, secret2, {
156
147
  in: "co_zTEST",
157
148
  tx: { sessionID: "co_zTEST_session_zTEST" as SessionID, txIndex: 1 },
158
149
  });
@@ -161,34 +152,37 @@ test("Encryption for transactions doesn't decrypt with a wrong key", () => {
161
152
  });
162
153
 
163
154
  test("Encryption of keySecrets round-trips", () => {
164
- const toEncrypt = newRandomKeySecret();
165
- const encrypting = newRandomKeySecret();
155
+ const toEncrypt = Crypto.newRandomKeySecret();
156
+ const encrypting = Crypto.newRandomKeySecret();
166
157
 
167
158
  const keys = {
168
159
  toEncrypt,
169
160
  encrypting,
170
161
  };
171
162
 
172
- const encrypted = encryptKeySecret(keys);
163
+ const encrypted = Crypto.encryptKeySecret(keys);
173
164
 
174
- const decrypted = decryptKeySecret(encrypted, encrypting.secret);
165
+ const decrypted = Crypto.decryptKeySecret(encrypted, encrypting.secret);
175
166
 
176
167
  expect(decrypted).toEqual(toEncrypt.secret);
177
168
  });
178
169
 
179
170
  test("Encryption of keySecrets doesn't decrypt with a wrong key", () => {
180
- const toEncrypt = newRandomKeySecret();
181
- const encrypting = newRandomKeySecret();
182
- const encryptingWrong = newRandomKeySecret();
171
+ const toEncrypt = Crypto.newRandomKeySecret();
172
+ const encrypting = Crypto.newRandomKeySecret();
173
+ const encryptingWrong = Crypto.newRandomKeySecret();
183
174
 
184
175
  const keys = {
185
176
  toEncrypt,
186
177
  encrypting,
187
178
  };
188
179
 
189
- const encrypted = encryptKeySecret(keys);
180
+ const encrypted = Crypto.encryptKeySecret(keys);
190
181
 
191
- const decrypted = decryptKeySecret(encrypted, encryptingWrong.secret);
182
+ const decrypted = Crypto.decryptKeySecret(
183
+ encrypted,
184
+ encryptingWrong.secret,
185
+ );
192
186
 
193
187
  expect(decrypted).toBeUndefined();
194
188
  });