ox 1.1.2 → 1.2.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 (131) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/core/Hash.d.ts +36 -0
  3. package/dist/core/Hash.d.ts.map +1 -1
  4. package/dist/core/Hash.js +35 -0
  5. package/dist/core/Hash.js.map +1 -1
  6. package/dist/node/Hash.d.ts +3 -3
  7. package/dist/node/Hash.js +3 -3
  8. package/dist/trusted-setups/Paths.d.ts +1 -0
  9. package/dist/trusted-setups/Paths.d.ts.map +1 -1
  10. package/dist/trusted-setups/Paths.js +1 -0
  11. package/dist/trusted-setups/Paths.js.map +1 -1
  12. package/dist/trusted-setups/Setups.d.ts +35 -0
  13. package/dist/trusted-setups/Setups.d.ts.map +1 -0
  14. package/dist/trusted-setups/Setups.js +29 -0
  15. package/dist/trusted-setups/Setups.js.map +1 -0
  16. package/dist/trusted-setups/index.d.ts +20 -0
  17. package/dist/trusted-setups/index.d.ts.map +1 -1
  18. package/dist/trusted-setups/index.js +20 -0
  19. package/dist/trusted-setups/index.js.map +1 -1
  20. package/dist/trusted-setups/internal/paths.d.ts.map +1 -1
  21. package/dist/trusted-setups/internal/paths.js +1 -1
  22. package/dist/trusted-setups/internal/paths.js.map +1 -1
  23. package/dist/trusted-setups/internal/setups/mainnet.d.ts +7 -0
  24. package/dist/trusted-setups/internal/setups/mainnet.d.ts.map +1 -0
  25. package/dist/trusted-setups/internal/setups/mainnet.js +11 -0
  26. package/dist/trusted-setups/internal/setups/mainnet.js.map +1 -0
  27. package/dist/wasm/Engine.d.ts +3 -2
  28. package/dist/wasm/Engine.d.ts.map +1 -1
  29. package/dist/wasm/Engine.js +5 -5
  30. package/dist/wasm/Engine.js.map +1 -1
  31. package/dist/wasm/Keystore.d.ts +23 -9
  32. package/dist/wasm/Keystore.d.ts.map +1 -1
  33. package/dist/wasm/Keystore.js +30 -13
  34. package/dist/wasm/Keystore.js.map +1 -1
  35. package/dist/wasm/Kzg.d.ts +112 -0
  36. package/dist/wasm/Kzg.d.ts.map +1 -0
  37. package/dist/wasm/Kzg.js +333 -0
  38. package/dist/wasm/Kzg.js.map +1 -0
  39. package/dist/wasm/Secp256k1.d.ts +55 -0
  40. package/dist/wasm/Secp256k1.d.ts.map +1 -0
  41. package/dist/wasm/Secp256k1.js +181 -0
  42. package/dist/wasm/Secp256k1.js.map +1 -0
  43. package/dist/wasm/index.d.ts +44 -1
  44. package/dist/wasm/index.d.ts.map +1 -1
  45. package/dist/wasm/index.js +44 -1
  46. package/dist/wasm/index.js.map +1 -1
  47. package/dist/wasm/internal/instantiate.d.ts +12 -0
  48. package/dist/wasm/internal/instantiate.d.ts.map +1 -1
  49. package/dist/wasm/internal/instantiate.js +23 -7
  50. package/dist/wasm/internal/instantiate.js.map +1 -1
  51. package/dist/wasm/internal/keystore.d.ts +10 -0
  52. package/dist/wasm/internal/keystore.d.ts.map +1 -0
  53. package/dist/wasm/internal/keystore.js +10 -0
  54. package/dist/wasm/internal/keystore.js.map +1 -0
  55. package/dist/wasm/internal/kzg.d.ts +17 -0
  56. package/dist/wasm/internal/kzg.d.ts.map +1 -0
  57. package/dist/wasm/internal/kzg.js +8 -0
  58. package/dist/wasm/internal/kzg.js.map +1 -0
  59. package/dist/wasm/internal/kzg.wasm.d.ts +3 -0
  60. package/dist/wasm/internal/kzg.wasm.d.ts.map +1 -0
  61. package/dist/wasm/internal/kzg.wasm.js +10 -0
  62. package/dist/wasm/internal/kzg.wasm.js.map +1 -0
  63. package/dist/wasm/internal/scrypt.d.ts +16 -0
  64. package/dist/wasm/internal/scrypt.d.ts.map +1 -0
  65. package/dist/wasm/internal/scrypt.js +78 -0
  66. package/dist/wasm/internal/scrypt.js.map +1 -0
  67. package/dist/wasm/internal/scrypt.wasm.d.ts +7 -0
  68. package/dist/wasm/internal/scrypt.wasm.d.ts.map +1 -0
  69. package/dist/wasm/internal/scrypt.wasm.js +14 -0
  70. package/dist/wasm/internal/scrypt.wasm.js.map +1 -0
  71. package/dist/wasm/internal/secp256k1.d.ts +16 -0
  72. package/dist/wasm/internal/secp256k1.d.ts.map +1 -0
  73. package/dist/wasm/internal/secp256k1.js +20 -0
  74. package/dist/wasm/internal/secp256k1.js.map +1 -0
  75. package/dist/wasm/internal/secp256k1.wasm.d.ts +3 -0
  76. package/dist/wasm/internal/secp256k1.wasm.d.ts.map +1 -0
  77. package/dist/wasm/internal/secp256k1.wasm.js +10 -0
  78. package/dist/wasm/internal/secp256k1.wasm.js.map +1 -0
  79. package/package.json +19 -2
  80. package/src/core/Hash.ts +59 -0
  81. package/src/core/_test/Block.test.ts +9 -2
  82. package/src/core/_test/Hash.test-d.ts +18 -0
  83. package/src/core/_test/Hash.test.ts +100 -1
  84. package/src/core/_test/Hash.vectors.test.ts +1 -2
  85. package/src/core/_test/Transaction.test.ts +11 -2
  86. package/src/node/Hash.ts +3 -3
  87. package/src/node/_test/Hash.test-d.ts +1 -0
  88. package/src/trusted-setups/Paths.ts +1 -0
  89. package/src/trusted-setups/README.md +39 -0
  90. package/src/trusted-setups/Setups.ts +46 -0
  91. package/src/trusted-setups/_test/Paths.test.ts +16 -0
  92. package/src/trusted-setups/_test/Setups.test-d.ts +7 -0
  93. package/src/trusted-setups/_test/Setups.test.ts +43 -0
  94. package/src/trusted-setups/_test/index.test.ts +1 -0
  95. package/src/trusted-setups/index.ts +21 -0
  96. package/src/trusted-setups/internal/paths.ts +4 -1
  97. package/src/trusted-setups/internal/setups/mainnet.source.json +7 -0
  98. package/src/trusted-setups/internal/setups/mainnet.ts +16 -0
  99. package/src/version.ts +1 -1
  100. package/src/wasm/Engine.ts +8 -6
  101. package/src/wasm/Keystore.bench.ts +26 -0
  102. package/src/wasm/Keystore.ts +32 -14
  103. package/src/wasm/Kzg.bench.ts +63 -0
  104. package/src/wasm/Kzg.ts +518 -0
  105. package/src/wasm/Secp256k1.bench.ts +79 -0
  106. package/src/wasm/Secp256k1.ts +248 -0
  107. package/src/wasm/_test/Engine.test-d.ts +1 -0
  108. package/src/wasm/_test/Hash.browser.test.ts +3 -0
  109. package/src/wasm/_test/Hash.test-d.ts +1 -0
  110. package/src/wasm/_test/Hash.test.ts +2 -2
  111. package/src/wasm/_test/Keystore.browser.test.ts +31 -0
  112. package/src/wasm/_test/Keystore.fuzz.ts +50 -0
  113. package/src/wasm/_test/Keystore.test-d.ts +10 -1
  114. package/src/wasm/_test/Keystore.test.ts +289 -4
  115. package/src/wasm/_test/Kzg.test-d.ts +19 -0
  116. package/src/wasm/_test/Kzg.test.ts +220 -0
  117. package/src/wasm/_test/Runtime.test.ts +9 -0
  118. package/src/wasm/_test/Secp256k1.test-d.ts +35 -0
  119. package/src/wasm/_test/Secp256k1.test.ts +354 -0
  120. package/src/wasm/index.ts +46 -1
  121. package/src/wasm/internal/instantiate.ts +29 -9
  122. package/src/wasm/internal/keystore.ts +19 -0
  123. package/src/wasm/internal/kzg.ts +50 -0
  124. package/src/wasm/internal/kzg.wasm.ts +11 -0
  125. package/src/wasm/internal/scrypt.ts +131 -0
  126. package/src/wasm/internal/scrypt.wasm.ts +17 -0
  127. package/src/wasm/internal/secp256k1.ts +62 -0
  128. package/src/wasm/internal/secp256k1.wasm.ts +11 -0
  129. package/wasm/vendor/c-kzg-4844/LICENSE +201 -0
  130. package/wasm/vendor/c-kzg-4844/blst/LICENSE +201 -0
  131. package/src/trusted-setups/internal/setups/mainnet.txt +0 -4163
@@ -0,0 +1,354 @@
1
+ import { secp256k1 } from '@noble/curves/secp256k1.js'
2
+ import { describe, expect, test } from 'vp/test'
3
+ import * as Secp256k1 from '../Secp256k1.js'
4
+ import * as secp256k1_internal from '../internal/secp256k1.js'
5
+
6
+ const privateKey = fromHex(
7
+ 'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
8
+ )
9
+ const privateKeyB = fromHex(
10
+ '59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d',
11
+ )
12
+ const curveOrder = BigInt(
13
+ '0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141',
14
+ )
15
+
16
+ describe('engine', () => {
17
+ test('behavior: exposes only deterministic primitives', async () => {
18
+ const engine = await Secp256k1.engine()
19
+
20
+ expect(Object.keys(engine).sort()).toMatchInlineSnapshot(`
21
+ [
22
+ "getPublicKey",
23
+ "getSharedSecret",
24
+ "recoverPublicKey",
25
+ "sign",
26
+ "verify",
27
+ ]
28
+ `)
29
+ })
30
+
31
+ test('behavior: matches public keys and compressed shared points', async () => {
32
+ const engine = await Secp256k1.engine()
33
+ const publicKey = secp256k1.getPublicKey(privateKey, false)
34
+ const publicKeyB = secp256k1.getPublicKey(privateKeyB, false)
35
+ const compressedPublicKeyB = secp256k1.getPublicKey(privateKeyB, true)
36
+ const expected = secp256k1.getSharedSecret(privateKey, publicKeyB, true)
37
+
38
+ expect(engine.getPublicKey(privateKey)).toEqual(publicKey)
39
+ expect(engine.getSharedSecret(privateKey, publicKeyB)).toEqual(expected)
40
+ expect(engine.getSharedSecret(privateKey, compressedPublicKeyB)).toEqual(
41
+ expected,
42
+ )
43
+ expect(engine.getSharedSecret(privateKeyB, publicKey)).toEqual(expected)
44
+ expect(expected).toHaveLength(33)
45
+ })
46
+
47
+ test('behavior: matches signing across payload and entropy boundaries', async () => {
48
+ const engine = await Secp256k1.engine()
49
+ const publicKey = secp256k1.getPublicKey(privateKey, false)
50
+
51
+ for (const size of [0, 1, 31, 32, 33, 63, 64, 65, 127, 1024, 8192])
52
+ for (const prehash of [false, true])
53
+ for (const extraEntropy of [
54
+ false,
55
+ new Uint8Array(),
56
+ Uint8Array.of(1),
57
+ new Uint8Array(32).fill(7),
58
+ new Uint8Array(100).fill(9),
59
+ ] as const) {
60
+ const payload = bytes(size)
61
+ const signature = engine.sign(payload, privateKey, {
62
+ extraEntropy,
63
+ prehash,
64
+ })
65
+ const expected = secp256k1.sign(payload, privateKey, {
66
+ extraEntropy,
67
+ format: 'recovered',
68
+ lowS: true,
69
+ prehash,
70
+ })
71
+
72
+ expect(signature).toEqual(expected)
73
+ expect(
74
+ engine.verify(signature.slice(1), payload, publicKey, { prehash }),
75
+ ).toBe(true)
76
+ expect(signature[0]).toBeOneOf([0, 1])
77
+ }
78
+ })
79
+
80
+ test('behavior: generates host entropy for hedged signing', async () => {
81
+ const engine = await Secp256k1.engine()
82
+ const payload = bytes(32)
83
+ const publicKey = engine.getPublicKey(privateKey)
84
+ const signatureA = engine.sign(payload, privateKey, {
85
+ extraEntropy: true,
86
+ prehash: false,
87
+ })
88
+ const signatureB = engine.sign(payload, privateKey, {
89
+ extraEntropy: true,
90
+ prehash: false,
91
+ })
92
+
93
+ expect(signatureA).not.toEqual(signatureB)
94
+ expect(
95
+ engine.verify(signatureA.slice(1), payload, publicKey, {
96
+ prehash: false,
97
+ }),
98
+ ).toBe(true)
99
+ expect(
100
+ engine.verify(signatureB.slice(1), payload, publicKey, {
101
+ prehash: false,
102
+ }),
103
+ ).toBe(true)
104
+ })
105
+
106
+ test('behavior: recovers the public key with both parities', async () => {
107
+ const engine = await Secp256k1.engine()
108
+ const publicKey = engine.getPublicKey(privateKey)
109
+ const signatures = new Map<
110
+ number,
111
+ { payload: Uint8Array; signature: Uint8Array }
112
+ >()
113
+
114
+ for (let index = 0; signatures.size < 2; index++) {
115
+ const payload = bytes(32, index)
116
+ const signature = engine.sign(payload, privateKey, {
117
+ extraEntropy: false,
118
+ prehash: false,
119
+ })
120
+ signatures.set(signature[0]!, { payload, signature })
121
+ if (index > 100)
122
+ throw new Error('could not produce both recovery parities')
123
+ }
124
+
125
+ for (const { payload, signature } of signatures.values())
126
+ expect(engine.recoverPublicKey(signature, payload)).toEqual(publicKey)
127
+ })
128
+
129
+ test('behavior: rejects high-S signatures', async () => {
130
+ const engine = await Secp256k1.engine()
131
+ const payload = bytes(32)
132
+ const publicKey = engine.getPublicKey(privateKey)
133
+ const signature = engine
134
+ .sign(payload, privateKey, {
135
+ extraEntropy: false,
136
+ prehash: false,
137
+ })
138
+ .slice(1)
139
+ const highS = signature.slice()
140
+ highS.set(toBytes(curveOrder - toBigInt(signature.slice(32))), 32)
141
+
142
+ expect(
143
+ engine.verify(signature, payload, publicKey, { prehash: false }),
144
+ ).toBe(true)
145
+ expect(engine.verify(highS, payload, publicKey, { prehash: false })).toBe(
146
+ false,
147
+ )
148
+ })
149
+
150
+ test('behavior: matches large payload boundaries', async () => {
151
+ const engine = await Secp256k1.engine()
152
+ const payload = bytes(8193)
153
+ const publicKey = engine.getPublicKey(privateKey)
154
+ const truncatedSignature = secp256k1.sign(
155
+ payload.slice(0, 32),
156
+ privateKey,
157
+ {
158
+ extraEntropy: false,
159
+ format: 'compact',
160
+ lowS: true,
161
+ prehash: false,
162
+ },
163
+ )
164
+
165
+ expect(() =>
166
+ engine.sign(payload, privateKey, {
167
+ extraEntropy: false,
168
+ prehash: false,
169
+ }),
170
+ ).toThrowError(
171
+ 'Secp256k1 unhashed payload must not exceed 8192 bytes, got 8193',
172
+ )
173
+ expect(
174
+ engine.verify(new Uint8Array(64), payload, publicKey, {
175
+ prehash: false,
176
+ }),
177
+ ).toBe(false)
178
+ expect(
179
+ secp256k1.verify(truncatedSignature, payload, publicKey, {
180
+ lowS: true,
181
+ prehash: false,
182
+ }),
183
+ ).toBe(false)
184
+ expect(
185
+ engine.verify(truncatedSignature, payload, publicKey, { prehash: false }),
186
+ ).toBe(false)
187
+ expect(() =>
188
+ engine.recoverPublicKey(new Uint8Array(65), payload),
189
+ ).toThrowError(
190
+ 'Secp256k1 unhashed payload must not exceed 8192 bytes, got 8193',
191
+ )
192
+
193
+ const signature = engine.sign(payload, privateKey, {
194
+ extraEntropy: false,
195
+ prehash: true,
196
+ })
197
+ expect(
198
+ engine.verify(signature.slice(1), payload, publicKey, { prehash: true }),
199
+ ).toBe(true)
200
+ })
201
+
202
+ test('behavior: rejects malformed keys and recovered signatures', async () => {
203
+ const engine = await Secp256k1.engine()
204
+ const publicKey = engine.getPublicKey(privateKey)
205
+ const payload = bytes(32)
206
+
207
+ for (const size of [0, 1, 31, 33, 64, 65]) {
208
+ if (size !== 32) {
209
+ expect(() => engine.getPublicKey(new Uint8Array(size))).toThrowError(
210
+ `Secp256k1 private key must be 32 bytes, got ${size}`,
211
+ )
212
+ expect(() =>
213
+ engine.sign(payload, new Uint8Array(size), {
214
+ extraEntropy: false,
215
+ prehash: false,
216
+ }),
217
+ ).toThrowError(`Secp256k1 private key must be 32 bytes, got ${size}`)
218
+ }
219
+ if (size !== 64)
220
+ expect(() =>
221
+ engine.verify(new Uint8Array(size), payload, publicKey, {
222
+ prehash: false,
223
+ }),
224
+ ).toThrowError(`Secp256k1 signature must be 64 bytes, got ${size}`)
225
+ if (size !== 65)
226
+ expect(() =>
227
+ engine.recoverPublicKey(new Uint8Array(size), payload),
228
+ ).toThrowError(`Secp256k1 signature must be 65 bytes, got ${size}`)
229
+ }
230
+
231
+ for (const size of [0, 1, 32, 34, 64, 66])
232
+ expect(() =>
233
+ engine.getSharedSecret(privateKey, new Uint8Array(size)),
234
+ ).toThrowError(`Secp256k1 public key must be 33 or 65 bytes, got ${size}`)
235
+
236
+ const invalidPrivateKeys = [
237
+ new Uint8Array(32),
238
+ toBytes(curveOrder),
239
+ toBytes(curveOrder + 1n),
240
+ ]
241
+ for (const value of invalidPrivateKeys) {
242
+ expect(() => engine.getPublicKey(value)).toThrow(
243
+ Secp256k1.InvalidInputError,
244
+ )
245
+ expect(() => engine.getSharedSecret(value, publicKey)).toThrow(
246
+ Secp256k1.InvalidInputError,
247
+ )
248
+ expect(() =>
249
+ engine.sign(payload, value, {
250
+ extraEntropy: false,
251
+ prehash: false,
252
+ }),
253
+ ).toThrow(Secp256k1.InvalidInputError)
254
+ }
255
+
256
+ for (const publicKeySize of [33, 65]) {
257
+ const invalidPublicKey = new Uint8Array(publicKeySize)
258
+ expect(() =>
259
+ engine.getSharedSecret(privateKey, invalidPublicKey),
260
+ ).toThrow(Secp256k1.InvalidInputError)
261
+ expect(
262
+ engine.verify(new Uint8Array(64), payload, invalidPublicKey, {
263
+ prehash: false,
264
+ }),
265
+ ).toBe(false)
266
+ }
267
+
268
+ const invalidRecovery = new Uint8Array(65)
269
+ invalidRecovery[0] = 2
270
+ expect(() => engine.recoverPublicKey(invalidRecovery, payload)).toThrow(
271
+ Secp256k1.InvalidInputError,
272
+ )
273
+ expect(() => engine.recoverPublicKey(new Uint8Array(65), payload)).toThrow(
274
+ Secp256k1.InvalidInputError,
275
+ )
276
+ expect(
277
+ engine.verify(new Uint8Array(64), payload, publicKey, {
278
+ prehash: false,
279
+ }),
280
+ ).toBe(false)
281
+ })
282
+
283
+ test('behavior: preserves subviews, input ownership, and output ownership', async () => {
284
+ const engine = await Secp256k1.engine()
285
+ const key = offsetView(privateKey)
286
+ const payload = offsetView(bytes(257))
287
+ const publicKey = offsetView(engine.getPublicKey(key))
288
+ const before = {
289
+ key: key.slice(),
290
+ payload: payload.slice(),
291
+ publicKey: publicKey.slice(),
292
+ }
293
+ const signature = engine.sign(payload, key, {
294
+ extraEntropy: false,
295
+ prehash: false,
296
+ })
297
+ const outputs = [
298
+ engine.getPublicKey(key),
299
+ engine.getSharedSecret(key, publicKey),
300
+ signature,
301
+ engine.recoverPublicKey(signature, payload),
302
+ ]
303
+ const snapshots = outputs.map((output) => output.slice())
304
+
305
+ engine.sign(new Uint8Array(1024 * 1024), key, {
306
+ extraEntropy: false,
307
+ prehash: true,
308
+ })
309
+
310
+ expect(outputs).toEqual(snapshots)
311
+ expect({ key, payload, publicKey }).toEqual(before)
312
+ })
313
+
314
+ test('behavior: clears the shared staging region', async () => {
315
+ const engine = await Secp256k1.engine()
316
+ const payload = new Uint8Array(1024).fill(0xa5)
317
+ const entropy = new Uint8Array(17).fill(0x5a)
318
+ engine.sign(payload, privateKey, {
319
+ extraEntropy: entropy,
320
+ prehash: false,
321
+ })
322
+
323
+ const module = await secp256k1_internal.load()
324
+ const size = payload.length + 32 + entropy.length + 65
325
+ expect(
326
+ module
327
+ .view()
328
+ .subarray(module.heapBase, module.heapBase + size)
329
+ .every((byte) => byte === 0),
330
+ ).toBe(true)
331
+ })
332
+ })
333
+
334
+ function bytes(length: number, offset = length): Uint8Array {
335
+ return Uint8Array.from({ length }, (_, index) => (index * 29 + offset) % 251)
336
+ }
337
+
338
+ function fromHex(value: string): Uint8Array {
339
+ return Uint8Array.from(Buffer.from(value, 'hex'))
340
+ }
341
+
342
+ function offsetView(value: Uint8Array): Uint8Array {
343
+ const bytes = new Uint8Array(value.length + 4).fill(0xff)
344
+ bytes.set(value, 2)
345
+ return bytes.subarray(2, -2)
346
+ }
347
+
348
+ function toBigInt(value: Uint8Array): bigint {
349
+ return BigInt(`0x${Buffer.from(value).toString('hex')}`)
350
+ }
351
+
352
+ function toBytes(value: bigint): Uint8Array {
353
+ return fromHex(value.toString(16).padStart(64, '0'))
354
+ }
package/src/wasm/index.ts CHANGED
@@ -64,7 +64,8 @@ export * as Ed25519 from './Ed25519.js'
64
64
  export * as Hash from './Hash.js'
65
65
 
66
66
  /**
67
- * WASM implementation of Ox's synchronous PBKDF2-HMAC-SHA256 primitive.
67
+ * WASM implementations of Ox's synchronous PBKDF2-HMAC-SHA256 and scrypt
68
+ * primitives.
68
69
  *
69
70
  * @example
70
71
  * ```ts twoslash
@@ -81,6 +82,29 @@ export * as Hash from './Hash.js'
81
82
  */
82
83
  export * as Keystore from './Keystore.js'
83
84
 
85
+ /**
86
+ * WASM implementation of Ox's KZG interface, backed by c-kzg-4844.
87
+ *
88
+ * This module is not installed into [`Engine`](/api/Engine). Create an explicit
89
+ * instance, pass it anywhere that accepts `Kzg.Kzg`, and dispose it when done.
90
+ *
91
+ * @example
92
+ * ```ts twoslash
93
+ * // @noErrors
94
+ * import { Setups } from 'ox/trusted-setups'
95
+ * import { Kzg } from 'ox/wasm'
96
+ *
97
+ * const kzg = await Kzg.create({
98
+ * trustedSetup: Setups.mainnet
99
+ * })
100
+ *
101
+ * kzg.dispose()
102
+ * ```
103
+ *
104
+ * @category Crypto
105
+ */
106
+ export * as Kzg from './Kzg.js'
107
+
84
108
  /**
85
109
  * WASM implementation of Ox's BIP-39 seed derivation.
86
110
  *
@@ -101,6 +125,27 @@ export * as Keystore from './Keystore.js'
101
125
  */
102
126
  export * as Mnemonic from './Mnemonic.js'
103
127
 
128
+ /**
129
+ * WASM implementations of deterministic Secp256k1 primitives.
130
+ *
131
+ * This provider is opt-in because its embedded libsecp256k1 artifact is larger
132
+ * than the modules installed by [`Engine.install`](/api/Engine/install).
133
+ *
134
+ * @example
135
+ * ```ts twoslash
136
+ * // @noErrors
137
+ * import { Engine } from 'ox'
138
+ * import { Secp256k1 } from 'ox/wasm'
139
+ *
140
+ * await Engine.install({ Secp256k1: Secp256k1.engine() })
141
+ *
142
+ * Secp256k1.getPublicKey({ privateKey: '0x...' })
143
+ * ```
144
+ *
145
+ * @category Crypto
146
+ */
147
+ export * as Secp256k1 from './Secp256k1.js'
148
+
104
149
  /**
105
150
  * WASM implementations of Ox's supported X25519 primitives.
106
151
  *
@@ -48,18 +48,23 @@ function decode(base64: string): Uint8Array {
48
48
  }
49
49
 
50
50
  /**
51
- * Instantiates a base64-encoded WASM module.
51
+ * Compiles a base64-encoded WASM module without instantiating it.
52
52
  *
53
- * Always asynchronous: browsers refuse to compile modules larger than a few
54
- * kilobytes synchronously on the main thread, so there is no synchronous path to
55
- * fall back to. Callers await this once and then call the module synchronously.
53
+ * @internal
54
+ */
55
+ export async function compile(base64: string): Promise<WebAssembly.Module> {
56
+ return WebAssembly.compile(decode(base64))
57
+ }
58
+
59
+ /**
60
+ * Instantiates a compiled WASM module.
56
61
  *
57
62
  * @internal
58
63
  */
59
- export async function instantiate<exports extends Record<string, unknown>>(
60
- base64: string,
61
- ): Promise<Module<exports>> {
62
- const { instance } = await WebAssembly.instantiate(decode(base64), {})
64
+ export async function instantiateModule<
65
+ exports extends Record<string, unknown>,
66
+ >(compiled: WebAssembly.Module): Promise<Module<exports>> {
67
+ const instance = await WebAssembly.instantiate(compiled, {})
63
68
  const exports = instance.exports as {
64
69
  heap_base(): number
65
70
  memory: WebAssembly.Memory
@@ -94,6 +99,21 @@ export async function instantiate<exports extends Record<string, unknown>>(
94
99
  }
95
100
  }
96
101
 
102
+ /**
103
+ * Instantiates a base64-encoded WASM module.
104
+ *
105
+ * Always asynchronous: browsers refuse to compile modules larger than a few
106
+ * kilobytes synchronously on the main thread, so there is no synchronous path to
107
+ * fall back to. Callers await this once and then call the module synchronously.
108
+ *
109
+ * @internal
110
+ */
111
+ export async function instantiate<exports extends Record<string, unknown>>(
112
+ base64: string,
113
+ ): Promise<Module<exports>> {
114
+ return instantiateModule(await compile(base64))
115
+ }
116
+
97
117
  /**
98
118
  * Memoizes a loader so concurrent callers compile the module once.
99
119
  *
@@ -128,7 +148,7 @@ export class MemoryError extends Errors.BaseError<Error> {
128
148
  cause,
129
149
  metaMessages: [
130
150
  `Required: ${bytes} bytes`,
131
- 'The input may be too large for this runtime. Reset the engine slot with `Engine.reset` to fall back to the default implementation.',
151
+ 'The input or requested workspace may be too large for this runtime. Reset the engine slot with `Engine.reset` to fall back to the default implementation.',
132
152
  ],
133
153
  })
134
154
  }
@@ -0,0 +1,19 @@
1
+ import type * as Engine from '../../core/Engine.js'
2
+ import * as hashes from './hashes.js'
3
+ import * as pbkdf2 from './pbkdf2.js'
4
+
5
+ /** Compiles the aggregate engine's synchronous PBKDF2 implementation. */
6
+ export async function engine(): Promise<engine.ReturnType> {
7
+ const module = await hashes.load()
8
+ return {
9
+ pbkdf2Sha256: (password, salt, options) =>
10
+ pbkdf2.pbkdf2Sha256(module, password, salt, options),
11
+ }
12
+ }
13
+
14
+ export declare namespace engine {
15
+ /** The aggregate WASM engine's `Keystore` primitives. */
16
+ type ReturnType = {
17
+ pbkdf2Sha256: NonNullable<Engine.Keystore['pbkdf2Sha256']>
18
+ }
19
+ }
@@ -0,0 +1,50 @@
1
+ import {
2
+ compile,
3
+ instantiateModule,
4
+ memoize,
5
+ type Module,
6
+ } from './instantiate.js'
7
+ import { wasmBase64 } from './kzg.wasm.js'
8
+
9
+ /** Exports supplied by the dedicated c-kzg artifact. @internal */
10
+ export type Exports = {
11
+ alloc(size: number): number
12
+ dealloc(ptr: number): void
13
+ kzg_blob_to_kzg_commitment(out: number, blob: number): number
14
+ kzg_compute_cells(cells: number, blob: number): number
15
+ kzg_compute_cells_and_kzg_proofs(
16
+ cells: number,
17
+ proofs: number,
18
+ blob: number,
19
+ ): number
20
+ kzg_dispose(): void
21
+ kzg_initialize(
22
+ g1Monomial: number,
23
+ g1Lagrange: number,
24
+ g2Monomial: number,
25
+ precompute: number,
26
+ ): number
27
+ kzg_recover_cells_and_kzg_proofs(
28
+ recoveredCells: number,
29
+ recoveredProofs: number,
30
+ cellIndices: number,
31
+ cells: number,
32
+ count: number,
33
+ ): number
34
+ kzg_verify_cell_kzg_proof_batch(
35
+ verified: number,
36
+ commitments: number,
37
+ cellIndices: number,
38
+ cells: number,
39
+ proofs: number,
40
+ count: number,
41
+ ): number
42
+ zero(ptr: number, length: number): void
43
+ }
44
+
45
+ const load = memoize(() => compile(wasmBase64))
46
+
47
+ /** Instantiates an independently owned c-kzg module. @internal */
48
+ export async function instantiate(): Promise<Module<Exports>> {
49
+ return instantiateModule(await load())
50
+ }