ox 1.1.0 → 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 (284) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/core/Engine.d.ts +43 -11
  3. package/dist/core/Engine.d.ts.map +1 -1
  4. package/dist/core/Engine.js +40 -10
  5. package/dist/core/Engine.js.map +1 -1
  6. package/dist/core/Hash.d.ts +36 -0
  7. package/dist/core/Hash.d.ts.map +1 -1
  8. package/dist/core/Hash.js +35 -0
  9. package/dist/core/Hash.js.map +1 -1
  10. package/dist/core/internal/engine.d.ts +8 -8
  11. package/dist/core/internal/engine.d.ts.map +1 -1
  12. package/dist/index.d.ts +5 -2
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +5 -2
  15. package/dist/index.js.map +1 -1
  16. package/dist/node/Ed25519.d.ts +33 -0
  17. package/dist/node/Ed25519.d.ts.map +1 -0
  18. package/dist/node/Ed25519.js +67 -0
  19. package/dist/node/Ed25519.js.map +1 -0
  20. package/dist/node/Engine.d.ts +29 -16
  21. package/dist/node/Engine.d.ts.map +1 -1
  22. package/dist/node/Engine.js +38 -14
  23. package/dist/node/Engine.js.map +1 -1
  24. package/dist/node/Hash.d.ts +17 -19
  25. package/dist/node/Hash.d.ts.map +1 -1
  26. package/dist/node/Hash.js +17 -19
  27. package/dist/node/Hash.js.map +1 -1
  28. package/dist/node/Keystore.d.ts +36 -0
  29. package/dist/node/Keystore.d.ts.map +1 -0
  30. package/dist/node/Keystore.js +72 -0
  31. package/dist/node/Keystore.js.map +1 -0
  32. package/dist/node/Mnemonic.d.ts +35 -0
  33. package/dist/node/Mnemonic.d.ts.map +1 -0
  34. package/dist/node/Mnemonic.js +46 -0
  35. package/dist/node/Mnemonic.js.map +1 -0
  36. package/dist/node/P256.d.ts +32 -0
  37. package/dist/node/P256.d.ts.map +1 -0
  38. package/dist/node/P256.js +34 -0
  39. package/dist/node/P256.js.map +1 -0
  40. package/dist/node/X25519.d.ts +31 -0
  41. package/dist/node/X25519.d.ts.map +1 -0
  42. package/dist/node/X25519.js +71 -0
  43. package/dist/node/X25519.js.map +1 -0
  44. package/dist/node/index.d.ts +95 -8
  45. package/dist/node/index.d.ts.map +1 -1
  46. package/dist/node/index.js +95 -8
  47. package/dist/node/index.js.map +1 -1
  48. package/dist/trusted-setups/Paths.d.ts +1 -0
  49. package/dist/trusted-setups/Paths.d.ts.map +1 -1
  50. package/dist/trusted-setups/Paths.js +1 -0
  51. package/dist/trusted-setups/Paths.js.map +1 -1
  52. package/dist/trusted-setups/Setups.d.ts +35 -0
  53. package/dist/trusted-setups/Setups.d.ts.map +1 -0
  54. package/dist/trusted-setups/Setups.js +29 -0
  55. package/dist/trusted-setups/Setups.js.map +1 -0
  56. package/dist/trusted-setups/index.d.ts +20 -0
  57. package/dist/trusted-setups/index.d.ts.map +1 -1
  58. package/dist/trusted-setups/index.js +20 -0
  59. package/dist/trusted-setups/index.js.map +1 -1
  60. package/dist/trusted-setups/internal/paths.d.ts.map +1 -1
  61. package/dist/trusted-setups/internal/paths.js +1 -1
  62. package/dist/trusted-setups/internal/paths.js.map +1 -1
  63. package/dist/trusted-setups/internal/setups/mainnet.d.ts +7 -0
  64. package/dist/trusted-setups/internal/setups/mainnet.d.ts.map +1 -0
  65. package/dist/trusted-setups/internal/setups/mainnet.js +11 -0
  66. package/dist/trusted-setups/internal/setups/mainnet.js.map +1 -0
  67. package/dist/wasm/Ed25519.d.ts +35 -0
  68. package/dist/wasm/Ed25519.d.ts.map +1 -0
  69. package/dist/wasm/Ed25519.js +88 -0
  70. package/dist/wasm/Ed25519.js.map +1 -0
  71. package/dist/wasm/Engine.d.ts +36 -26
  72. package/dist/wasm/Engine.d.ts.map +1 -1
  73. package/dist/wasm/Engine.js +44 -25
  74. package/dist/wasm/Engine.js.map +1 -1
  75. package/dist/wasm/Hash.d.ts +13 -19
  76. package/dist/wasm/Hash.d.ts.map +1 -1
  77. package/dist/wasm/Hash.js +21 -27
  78. package/dist/wasm/Hash.js.map +1 -1
  79. package/dist/wasm/Keystore.d.ts +51 -0
  80. package/dist/wasm/Keystore.d.ts.map +1 -0
  81. package/dist/wasm/Keystore.js +51 -0
  82. package/dist/wasm/Keystore.js.map +1 -0
  83. package/dist/wasm/Kzg.d.ts +112 -0
  84. package/dist/wasm/Kzg.d.ts.map +1 -0
  85. package/dist/wasm/Kzg.js +333 -0
  86. package/dist/wasm/Kzg.js.map +1 -0
  87. package/dist/wasm/Mnemonic.d.ts +33 -0
  88. package/dist/wasm/Mnemonic.d.ts.map +1 -0
  89. package/dist/wasm/Mnemonic.js +30 -0
  90. package/dist/wasm/Mnemonic.js.map +1 -0
  91. package/dist/wasm/Secp256k1.d.ts +55 -0
  92. package/dist/wasm/Secp256k1.d.ts.map +1 -0
  93. package/dist/wasm/Secp256k1.js +181 -0
  94. package/dist/wasm/Secp256k1.js.map +1 -0
  95. package/dist/wasm/X25519.d.ts +33 -0
  96. package/dist/wasm/X25519.d.ts.map +1 -0
  97. package/dist/wasm/X25519.js +53 -0
  98. package/dist/wasm/X25519.js.map +1 -0
  99. package/dist/wasm/index.d.ts +124 -12
  100. package/dist/wasm/index.d.ts.map +1 -1
  101. package/dist/wasm/index.js +124 -12
  102. package/dist/wasm/index.js.map +1 -1
  103. package/dist/wasm/internal/blake3.d.ts +11 -0
  104. package/dist/wasm/internal/blake3.d.ts.map +1 -0
  105. package/dist/wasm/internal/blake3.js +31 -0
  106. package/dist/wasm/internal/blake3.js.map +1 -0
  107. package/dist/wasm/internal/blake3.wasm.d.ts +3 -0
  108. package/dist/wasm/internal/blake3.wasm.d.ts.map +1 -0
  109. package/dist/wasm/internal/blake3.wasm.js +10 -0
  110. package/dist/wasm/internal/blake3.wasm.js.map +1 -0
  111. package/dist/wasm/internal/crypto25519.d.ts +16 -0
  112. package/dist/wasm/internal/crypto25519.d.ts.map +1 -0
  113. package/dist/wasm/internal/crypto25519.js +5 -0
  114. package/dist/wasm/internal/crypto25519.js.map +1 -0
  115. package/dist/wasm/internal/crypto25519.wasm.d.ts +3 -0
  116. package/dist/wasm/internal/crypto25519.wasm.d.ts.map +1 -0
  117. package/dist/wasm/internal/crypto25519.wasm.js +10 -0
  118. package/dist/wasm/internal/crypto25519.wasm.js.map +1 -0
  119. package/dist/wasm/internal/ed25519.d.ts +4 -0
  120. package/dist/wasm/internal/ed25519.d.ts.map +1 -0
  121. package/dist/wasm/internal/ed25519.js +21 -0
  122. package/dist/wasm/internal/ed25519.js.map +1 -0
  123. package/dist/wasm/internal/hashes.d.ts +12 -0
  124. package/dist/wasm/internal/hashes.d.ts.map +1 -0
  125. package/dist/wasm/internal/hashes.js +8 -0
  126. package/dist/wasm/internal/hashes.js.map +1 -0
  127. package/dist/wasm/internal/hashes.wasm.d.ts +4 -2
  128. package/dist/wasm/internal/hashes.wasm.d.ts.map +1 -1
  129. package/dist/wasm/internal/hashes.wasm.js +4 -2
  130. package/dist/wasm/internal/hashes.wasm.js.map +1 -1
  131. package/dist/wasm/internal/instantiate.d.ts +12 -0
  132. package/dist/wasm/internal/instantiate.d.ts.map +1 -1
  133. package/dist/wasm/internal/instantiate.js +23 -7
  134. package/dist/wasm/internal/instantiate.js.map +1 -1
  135. package/dist/wasm/internal/keystore.d.ts +10 -0
  136. package/dist/wasm/internal/keystore.d.ts.map +1 -0
  137. package/dist/wasm/internal/keystore.js +10 -0
  138. package/dist/wasm/internal/keystore.js.map +1 -0
  139. package/dist/wasm/internal/kzg.d.ts +17 -0
  140. package/dist/wasm/internal/kzg.d.ts.map +1 -0
  141. package/dist/wasm/internal/kzg.js +8 -0
  142. package/dist/wasm/internal/kzg.js.map +1 -0
  143. package/dist/wasm/internal/kzg.wasm.d.ts +3 -0
  144. package/dist/wasm/internal/kzg.wasm.d.ts.map +1 -0
  145. package/dist/wasm/internal/kzg.wasm.js +10 -0
  146. package/dist/wasm/internal/kzg.wasm.js.map +1 -0
  147. package/dist/wasm/internal/mnemonic.d.ts +4 -0
  148. package/dist/wasm/internal/mnemonic.d.ts.map +1 -0
  149. package/dist/wasm/internal/mnemonic.js +45 -0
  150. package/dist/wasm/internal/mnemonic.js.map +1 -0
  151. package/dist/wasm/internal/pbkdf2.d.ts +12 -0
  152. package/dist/wasm/internal/pbkdf2.d.ts.map +1 -0
  153. package/dist/wasm/internal/pbkdf2.js +51 -0
  154. package/dist/wasm/internal/pbkdf2.js.map +1 -0
  155. package/dist/wasm/internal/scrypt.d.ts +16 -0
  156. package/dist/wasm/internal/scrypt.d.ts.map +1 -0
  157. package/dist/wasm/internal/scrypt.js +78 -0
  158. package/dist/wasm/internal/scrypt.js.map +1 -0
  159. package/dist/wasm/internal/scrypt.wasm.d.ts +7 -0
  160. package/dist/wasm/internal/scrypt.wasm.d.ts.map +1 -0
  161. package/dist/wasm/internal/scrypt.wasm.js +14 -0
  162. package/dist/wasm/internal/scrypt.wasm.js.map +1 -0
  163. package/dist/wasm/internal/secp256k1.d.ts +16 -0
  164. package/dist/wasm/internal/secp256k1.d.ts.map +1 -0
  165. package/dist/wasm/internal/secp256k1.js +20 -0
  166. package/dist/wasm/internal/secp256k1.js.map +1 -0
  167. package/dist/wasm/internal/secp256k1.wasm.d.ts +3 -0
  168. package/dist/wasm/internal/secp256k1.wasm.d.ts.map +1 -0
  169. package/dist/wasm/internal/secp256k1.wasm.js +10 -0
  170. package/dist/wasm/internal/secp256k1.wasm.js.map +1 -0
  171. package/dist/wasm/internal/x25519.d.ts +4 -0
  172. package/dist/wasm/internal/x25519.d.ts.map +1 -0
  173. package/dist/wasm/internal/x25519.js +21 -0
  174. package/dist/wasm/internal/x25519.js.map +1 -0
  175. package/package.json +64 -2
  176. package/src/core/Engine.ts +84 -11
  177. package/src/core/Hash.ts +59 -0
  178. package/src/core/_test/Block.test.ts +9 -2
  179. package/src/core/_test/Engine.test-d.ts +56 -0
  180. package/src/core/_test/Engine.test.ts +95 -0
  181. package/src/core/_test/Hash.test-d.ts +18 -0
  182. package/src/core/_test/Hash.test.ts +100 -1
  183. package/src/core/_test/Hash.vectors.test.ts +7 -0
  184. package/src/core/_test/Transaction.test.ts +11 -2
  185. package/src/core/internal/engine.ts +8 -8
  186. package/src/index.ts +5 -2
  187. package/src/node/Ed25519.ts +87 -0
  188. package/src/node/Engine.ts +79 -23
  189. package/src/node/Hash.ts +25 -30
  190. package/src/node/Keystore.ts +103 -0
  191. package/src/node/Mnemonic.ts +68 -0
  192. package/src/node/P256.ts +48 -0
  193. package/src/node/X25519.ts +93 -0
  194. package/src/node/_test/Curves.fuzz.ts +76 -0
  195. package/src/node/_test/Ed25519.test-d.ts +35 -0
  196. package/src/node/_test/Ed25519.test.ts +220 -0
  197. package/src/node/_test/Engine.test-d.ts +28 -0
  198. package/src/node/_test/Engine.test.ts +62 -22
  199. package/src/node/_test/Hash.test-d.ts +16 -9
  200. package/src/node/_test/Hash.test.ts +10 -11
  201. package/src/node/_test/Keystore.fuzz.ts +58 -0
  202. package/src/node/_test/Keystore.test-d.ts +43 -0
  203. package/src/node/_test/Keystore.test.ts +198 -0
  204. package/src/node/_test/Mnemonic.fuzz.ts +31 -0
  205. package/src/node/_test/Mnemonic.test-d.ts +21 -0
  206. package/src/node/_test/Mnemonic.test.ts +98 -0
  207. package/src/node/_test/P256.test-d.ts +29 -0
  208. package/src/node/_test/P256.test.ts +102 -0
  209. package/src/node/_test/X25519.test-d.ts +28 -0
  210. package/src/node/_test/X25519.test.ts +239 -0
  211. package/src/node/index.ts +100 -8
  212. package/src/trusted-setups/Paths.ts +1 -0
  213. package/src/trusted-setups/README.md +39 -0
  214. package/src/trusted-setups/Setups.ts +46 -0
  215. package/src/trusted-setups/_test/Paths.test.ts +16 -0
  216. package/src/trusted-setups/_test/Setups.test-d.ts +7 -0
  217. package/src/trusted-setups/_test/Setups.test.ts +43 -0
  218. package/src/trusted-setups/_test/index.test.ts +1 -0
  219. package/src/trusted-setups/index.ts +21 -0
  220. package/src/trusted-setups/internal/paths.ts +4 -1
  221. package/src/trusted-setups/internal/setups/mainnet.source.json +7 -0
  222. package/src/trusted-setups/internal/setups/mainnet.ts +16 -0
  223. package/src/version.ts +1 -1
  224. package/src/wasm/Ed25519.ts +114 -0
  225. package/src/wasm/Engine.ts +78 -31
  226. package/src/wasm/Hash.bench.ts +2 -2
  227. package/src/wasm/Hash.ts +29 -50
  228. package/src/wasm/Keystore.bench.ts +26 -0
  229. package/src/wasm/Keystore.ts +66 -0
  230. package/src/wasm/Kzg.bench.ts +63 -0
  231. package/src/wasm/Kzg.ts +518 -0
  232. package/src/wasm/Mnemonic.ts +44 -0
  233. package/src/wasm/Secp256k1.bench.ts +79 -0
  234. package/src/wasm/Secp256k1.ts +248 -0
  235. package/src/wasm/X25519.ts +70 -0
  236. package/src/wasm/_test/Crypto25519.conformance.ts +57 -0
  237. package/src/wasm/_test/Crypto25519.fuzz.ts +187 -0
  238. package/src/wasm/_test/Ed25519.test-d.ts +28 -0
  239. package/src/wasm/_test/Ed25519.test.ts +168 -0
  240. package/src/wasm/_test/Ed25519.vectors.test.ts +73 -0
  241. package/src/wasm/_test/Engine.test-d.ts +29 -0
  242. package/src/wasm/_test/Engine.test.ts +59 -25
  243. package/src/wasm/_test/Hash.browser.test.ts +14 -7
  244. package/src/wasm/_test/Hash.fuzz.ts +58 -23
  245. package/src/wasm/_test/Hash.test-d.ts +16 -10
  246. package/src/wasm/_test/Hash.test.ts +148 -48
  247. package/src/wasm/_test/Hash.vectors.test.ts +15 -8
  248. package/src/wasm/_test/Keystore.browser.test.ts +73 -0
  249. package/src/wasm/_test/Keystore.fuzz.ts +163 -0
  250. package/src/wasm/_test/Keystore.test-d.ts +42 -0
  251. package/src/wasm/_test/Keystore.test.ts +477 -0
  252. package/src/wasm/_test/Keystore.vectors.test.ts +21 -0
  253. package/src/wasm/_test/Kzg.test-d.ts +19 -0
  254. package/src/wasm/_test/Kzg.test.ts +220 -0
  255. package/src/wasm/_test/Mnemonic.test-d.ts +17 -0
  256. package/src/wasm/_test/Mnemonic.test.ts +110 -0
  257. package/src/wasm/_test/Runtime.test.ts +9 -0
  258. package/src/wasm/_test/Secp256k1.test-d.ts +35 -0
  259. package/src/wasm/_test/Secp256k1.test.ts +354 -0
  260. package/src/wasm/_test/X25519.test-d.ts +21 -0
  261. package/src/wasm/_test/X25519.test.ts +194 -0
  262. package/src/wasm/_test/fixtures.ts +4 -0
  263. package/src/wasm/index.ts +130 -12
  264. package/src/wasm/internal/blake3.ts +45 -0
  265. package/src/wasm/internal/blake3.wasm.ts +11 -0
  266. package/src/wasm/internal/crypto25519.ts +43 -0
  267. package/src/wasm/internal/crypto25519.wasm.ts +11 -0
  268. package/src/wasm/internal/ed25519.ts +31 -0
  269. package/src/wasm/internal/hashes.ts +29 -0
  270. package/src/wasm/internal/hashes.wasm.ts +5 -2
  271. package/src/wasm/internal/instantiate.ts +29 -9
  272. package/src/wasm/internal/keystore.ts +19 -0
  273. package/src/wasm/internal/kzg.ts +50 -0
  274. package/src/wasm/internal/kzg.wasm.ts +11 -0
  275. package/src/wasm/internal/mnemonic.ts +56 -0
  276. package/src/wasm/internal/pbkdf2.ts +86 -0
  277. package/src/wasm/internal/scrypt.ts +131 -0
  278. package/src/wasm/internal/scrypt.wasm.ts +17 -0
  279. package/src/wasm/internal/secp256k1.ts +62 -0
  280. package/src/wasm/internal/secp256k1.wasm.ts +11 -0
  281. package/src/wasm/internal/x25519.ts +29 -0
  282. package/wasm/vendor/c-kzg-4844/LICENSE +201 -0
  283. package/wasm/vendor/c-kzg-4844/blst/LICENSE +201 -0
  284. package/src/trusted-setups/internal/setups/mainnet.txt +0 -4163
@@ -0,0 +1,168 @@
1
+ import { ed25519 } from '@noble/curves/ed25519.js'
2
+ import { describe, expect, test } from 'vp/test'
3
+ import * as Ed25519 from '../Ed25519.js'
4
+ import * as crypto25519 from '../internal/crypto25519.js'
5
+ import { wasmBase64 } from '../internal/crypto25519.wasm.js'
6
+ import * as ed25519_internal from '../internal/ed25519.js'
7
+ import * as internal from '../internal/instantiate.js'
8
+
9
+ const privateKey = fromHex(
10
+ '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60',
11
+ )
12
+
13
+ describe('engine', () => {
14
+ test('behavior: exposes only semantics-compatible primitives', async () => {
15
+ const engine = await Ed25519.engine()
16
+
17
+ expect(Object.keys(engine).sort()).toMatchInlineSnapshot(`
18
+ [
19
+ "getPublicKey",
20
+ "sign",
21
+ "toMontgomerySecret",
22
+ "verify",
23
+ ]
24
+ `)
25
+ })
26
+
27
+ test('behavior: signs and verifies boundary-sized messages', async () => {
28
+ const engine = await Ed25519.engine()
29
+
30
+ for (const size of [0, 1, 31, 32, 63, 64, 65, 127, 128, 129, 1024]) {
31
+ const payload = Uint8Array.from(
32
+ { length: size },
33
+ (_, index) => (index * 29 + size) % 251,
34
+ )
35
+ const signature = engine.sign(payload, privateKey)
36
+ const publicKey = engine.getPublicKey(privateKey)
37
+
38
+ expect(signature).toEqual(ed25519.sign(payload, privateKey))
39
+ expect(engine.verify(signature, payload, publicKey)).toBe(true)
40
+ expect(
41
+ engine.verify(signature, Uint8Array.of(...payload, 1), publicKey),
42
+ ).toBe(false)
43
+ }
44
+ })
45
+
46
+ test('behavior: matches private-key conversion', async () => {
47
+ const engine = await Ed25519.engine()
48
+
49
+ expect(engine.toMontgomerySecret(privateKey)).toEqual(
50
+ ed25519.utils.toMontgomerySecret(privateKey),
51
+ )
52
+ })
53
+
54
+ test('behavior: respects subviews and leaves inputs immutable', async () => {
55
+ const engine = await Ed25519.engine()
56
+ const key = offsetView(privateKey)
57
+ const payload = offsetView(new Uint8Array(257).fill(0xa5))
58
+ const keyBefore = key.slice()
59
+ const payloadBefore = payload.slice()
60
+ const publicKey = offsetView(engine.getPublicKey(key))
61
+ const signature = offsetView(engine.sign(payload, key))
62
+ const publicKeyBefore = publicKey.slice()
63
+ const signatureBefore = signature.slice()
64
+
65
+ expect(engine.getPublicKey(key)).toEqual(ed25519.getPublicKey(key))
66
+ expect(engine.verify(signature, payload, publicKey)).toBe(true)
67
+ expect(engine.toMontgomerySecret(key)).toEqual(
68
+ ed25519.utils.toMontgomerySecret(key),
69
+ )
70
+ expect({ key, payload, publicKey, signature }).toEqual({
71
+ key: keyBefore,
72
+ payload: payloadBefore,
73
+ publicKey: publicKeyBefore,
74
+ signature: signatureBefore,
75
+ })
76
+ })
77
+
78
+ test('behavior: rejects malformed key and signature lengths', async () => {
79
+ const engine = await Ed25519.engine()
80
+ const publicKey = engine.getPublicKey(privateKey)
81
+
82
+ for (const size of [0, 1, 31, 33, 63, 64]) {
83
+ if (size !== 32) {
84
+ expect(() => engine.getPublicKey(new Uint8Array(size))).toThrowError(
85
+ `Ed25519 private key must be 32 bytes, got ${size}`,
86
+ )
87
+ expect(() =>
88
+ engine.sign(new Uint8Array(), new Uint8Array(size)),
89
+ ).toThrowError(`Ed25519 private key must be 32 bytes, got ${size}`)
90
+ }
91
+ if (size !== 64)
92
+ expect(() =>
93
+ engine.verify(new Uint8Array(size), new Uint8Array(), publicKey),
94
+ ).toThrowError(`Ed25519 signature must be 64 bytes, got ${size}`)
95
+ }
96
+
97
+ expect(() =>
98
+ engine.verify(new Uint8Array(64), new Uint8Array(), new Uint8Array(31)),
99
+ ).toThrowError('Ed25519 public key must be 32 bytes, got 31')
100
+ })
101
+
102
+ test('behavior: outputs are owned across calls and memory growth', async () => {
103
+ const engine = await Ed25519.engine()
104
+ const publicKey = engine.getPublicKey(privateKey)
105
+ const signature = engine.sign(new Uint8Array(), privateKey)
106
+ const snapshots = [publicKey.slice(), signature.slice()]
107
+
108
+ engine.sign(new Uint8Array(1024 * 1024).fill(0x5a), privateKey)
109
+
110
+ expect([publicKey, signature]).toEqual(snapshots)
111
+ expect(engine.getPublicKey(privateKey) === publicKey).toBe(false)
112
+ })
113
+
114
+ test('behavior: clears the shared staging region', async () => {
115
+ const engine = await Ed25519.engine()
116
+ const payload = new Uint8Array(1024).fill(0xa5)
117
+ engine.sign(payload, privateKey)
118
+
119
+ const module = await crypto25519.load()
120
+ const size = 32 + payload.length + 64
121
+ expect(
122
+ module
123
+ .view()
124
+ .subarray(module.heapBase, module.heapBase + size)
125
+ .every((byte) => byte === 0),
126
+ ).toBe(true)
127
+ })
128
+
129
+ test('behavior: clears the complete region after a late trap', async () => {
130
+ const module = await internal.instantiate<crypto25519.Exports>(wasmBase64)
131
+ const payload = new Uint8Array(1024).fill(0xa5)
132
+ const exports: crypto25519.Exports = {
133
+ ...module.exports,
134
+ ed25519_sign(...parameters) {
135
+ module.exports.ed25519_sign(...parameters)
136
+ throw new WebAssembly.RuntimeError('forced late trap')
137
+ },
138
+ }
139
+
140
+ expect(() =>
141
+ ed25519_internal.sign({ ...module, exports }, payload, privateKey),
142
+ ).toThrow(WebAssembly.RuntimeError)
143
+ expect(
144
+ module
145
+ .view()
146
+ .subarray(module.heapBase, module.heapBase + 32 + payload.length + 64)
147
+ .every((byte) => byte === 0),
148
+ ).toBe(true)
149
+ })
150
+
151
+ test('behavior: rejects a workspace beyond wasm32 without growing', async () => {
152
+ const module = await internal.instantiate<crypto25519.Exports>(wasmBase64)
153
+ const memory = module.view().length
154
+
155
+ expect(() => module.reserve(0x1_0000_0000)).toThrow(internal.MemoryError)
156
+ expect(module.view()).toHaveLength(memory)
157
+ })
158
+ })
159
+
160
+ function fromHex(value: string): Uint8Array {
161
+ return Uint8Array.from(Buffer.from(value, 'hex'))
162
+ }
163
+
164
+ function offsetView(value: Uint8Array): Uint8Array {
165
+ const bytes = new Uint8Array(value.length + 4).fill(0xff)
166
+ bytes.set(value, 2)
167
+ return bytes.subarray(2, -2)
168
+ }
@@ -0,0 +1,73 @@
1
+ import * as fs from 'node:fs'
2
+ import { describe, expect, test } from 'vp/test'
3
+ import * as Ed25519 from '../Ed25519.js'
4
+
5
+ type Zip215Vector = {
6
+ sig_bytes: string
7
+ valid_legacy: boolean
8
+ valid_zip215: boolean
9
+ vk_bytes: string
10
+ }
11
+
12
+ const zip215 = JSON.parse(
13
+ fs.readFileSync(
14
+ new URL('../../../test/vectors/ed25519/zip215.json', import.meta.url),
15
+ 'utf8',
16
+ ),
17
+ ) as readonly Zip215Vector[]
18
+
19
+ const rfc8032 = [
20
+ {
21
+ message: '',
22
+ publicKey:
23
+ 'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a',
24
+ seed: '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60',
25
+ signature:
26
+ 'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b',
27
+ },
28
+ {
29
+ message: '72',
30
+ publicKey:
31
+ '3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c',
32
+ seed: '4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb',
33
+ signature:
34
+ '92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00',
35
+ },
36
+ ] as const
37
+
38
+ describe('engine', () => {
39
+ test('vectors: matches RFC 8032 key, signature, and verification cases', async () => {
40
+ const engine = await Ed25519.engine()
41
+
42
+ for (const vector of rfc8032) {
43
+ const seed = fromHex(vector.seed)
44
+ const message = fromHex(vector.message)
45
+ const publicKey = fromHex(vector.publicKey)
46
+ const signature = fromHex(vector.signature)
47
+
48
+ expect(engine.getPublicKey(seed)).toEqual(publicKey)
49
+ expect(engine.sign(message, seed)).toEqual(signature)
50
+ expect(engine.verify(signature, message, publicKey)).toBe(true)
51
+ }
52
+ })
53
+
54
+ test('vectors: matches all 196 ZIP-215 compliance cases', async () => {
55
+ const engine = await Ed25519.engine()
56
+ const message = new TextEncoder().encode('Zcash')
57
+
58
+ expect(zip215).toHaveLength(196)
59
+ for (const vector of zip215)
60
+ expect(
61
+ engine.verify(
62
+ fromHex(vector.sig_bytes),
63
+ message,
64
+ fromHex(vector.vk_bytes),
65
+ ),
66
+ JSON.stringify(vector),
67
+ ).toBe(vector.valid_zip215)
68
+ })
69
+ })
70
+
71
+ function fromHex(value: string): Uint8Array {
72
+ return Uint8Array.from(Buffer.from(value, 'hex'))
73
+ }
@@ -0,0 +1,29 @@
1
+ import { Engine } from 'ox/wasm'
2
+ import { expectTypeOf, test } from 'vp/test'
3
+ import * as CoreEngine from '../../core/Engine.js'
4
+
5
+ type Installed = Awaited<ReturnType<typeof Engine.install>>
6
+ type Uninstalled = Awaited<ReturnType<typeof Engine.engine>>
7
+
8
+ test('install and engine expose the same precise engine', () => {
9
+ expectTypeOf<Installed>().toEqualTypeOf<Uninstalled>()
10
+ expectTypeOf<Installed['Hash']['sha256']>().toEqualTypeOf<
11
+ (input: Uint8Array) => Uint8Array
12
+ >()
13
+ expectTypeOf<Installed['Keystore']>().not.toHaveProperty('scrypt')
14
+ })
15
+
16
+ test('the WASM Engine namespace exposes synchronous core operations', () => {
17
+ expectTypeOf(Engine.get).toEqualTypeOf(CoreEngine.get)
18
+ expectTypeOf(Engine.InvalidSlotValueError).toEqualTypeOf(
19
+ CoreEngine.InvalidSlotValueError,
20
+ )
21
+ expectTypeOf(Engine.reset).toEqualTypeOf(CoreEngine.reset)
22
+ expectTypeOf(Engine.set).toEqualTypeOf(CoreEngine.set)
23
+ expectTypeOf(Engine.with).toEqualTypeOf(CoreEngine.with)
24
+ })
25
+
26
+ test('the replaced aliases are absent', () => {
27
+ expectTypeOf<typeof Engine>().not.toHaveProperty('create')
28
+ expectTypeOf<typeof Engine>().not.toHaveProperty('load')
29
+ })
@@ -1,62 +1,96 @@
1
- import { Engine as CoreEngine, Hash } from 'ox'
2
- import { Engine } from 'ox/wasm'
1
+ import { Engine as CoreEngine } from 'ox'
2
+ import { Engine, Hash } from 'ox/wasm'
3
3
  import { describe, expect, test } from 'vp/test'
4
4
 
5
- describe('create', () => {
5
+ describe('engine', () => {
6
6
  test('behavior: does not install', async () => {
7
- const engine = await Engine.create()
7
+ const engine = await Engine.engine()
8
8
  expect(Object.keys(engine)).toMatchInlineSnapshot(`
9
9
  [
10
+ "Ed25519",
10
11
  "Hash",
12
+ "Keystore",
13
+ "Mnemonic",
14
+ "X25519",
11
15
  ]
12
16
  `)
13
- // The whole point of `create` over `load`: benches, differential tests and
14
- // composition all need the implementation without touching global state.
17
+ // Benches, differential tests, and composition need an implementation
18
+ // value without touching global state.
15
19
  expect(CoreEngine.get()).toMatchInlineSnapshot('{}')
16
20
  })
17
21
 
18
22
  test('behavior: installs for the duration of a call', async () => {
19
23
  // `with` is the case that genuinely needs a value: there is nothing
20
24
  // installed for `set` to merge into.
21
- const wasm = await Engine.create()
22
- const inside = CoreEngine.with(wasm, () => Hash.keccak256('0xdeadbeef'))
23
- expect(inside).toEqual(Hash.keccak256('0xdeadbeef'))
25
+ const wasm = await Engine.engine()
26
+ const inside = CoreEngine.with(wasm, () => Hash.sha256('0xdeadbeef'))
27
+ expect(inside).toEqual(Hash.sha256('0xdeadbeef'))
24
28
  expect(CoreEngine.get()).toMatchInlineSnapshot('{}')
25
29
  })
26
30
  })
27
31
 
28
- describe('load', () => {
32
+ describe('install', () => {
29
33
  test('behavior: merges with a later set, rather than being replaced', async () => {
30
- await Engine.load()
31
- CoreEngine.set({ Mnemonic: { toSeed: () => new Uint8Array(64) } })
34
+ await Engine.install()
35
+ CoreEngine.set({ Bls: { randomSecretKey: () => new Uint8Array(32) } })
32
36
  expect(Object.keys(CoreEngine.get()).sort()).toMatchInlineSnapshot(`
33
37
  [
38
+ "Bls",
39
+ "Ed25519",
34
40
  "Hash",
41
+ "Keystore",
35
42
  "Mnemonic",
43
+ "X25519",
36
44
  ]
37
45
  `)
38
46
  })
39
47
 
40
48
  test('behavior: installs, and returns what it installed', async () => {
41
- const before = Hash.keccak256('0xdeadbeef')
49
+ const before = Hash.sha256('0xdeadbeef')
42
50
 
43
- const engine = await Engine.load()
51
+ const engine = await Engine.install()
44
52
 
45
53
  expect(Object.keys(CoreEngine.get())).toEqual(Object.keys(engine))
46
54
  // Installing changes which implementation runs, never the answer.
47
- expect(Hash.keccak256('0xdeadbeef')).toEqual(before)
55
+ expect(Hash.sha256('0xdeadbeef')).toEqual(before)
56
+ expect(Engine.get()).toEqual(CoreEngine.get())
48
57
  })
49
58
 
50
59
  test('behavior: routes every slot it reports', async () => {
51
- await Engine.load()
52
- expect(Object.keys(CoreEngine.get().Hash ?? {}).sort())
53
- .toMatchInlineSnapshot(`
54
- [
55
- "hmacSha256",
56
- "keccak256",
57
- "ripemd160",
58
- "sha256",
59
- ]
60
- `)
60
+ await Engine.install()
61
+ expect(
62
+ Object.fromEntries(
63
+ Object.entries(CoreEngine.get()).map(([slot, value]) => [
64
+ slot,
65
+ Object.keys(value ?? {}).sort(),
66
+ ]),
67
+ ),
68
+ ).toMatchInlineSnapshot(`
69
+ {
70
+ "Ed25519": [
71
+ "getPublicKey",
72
+ "sign",
73
+ "toMontgomerySecret",
74
+ "verify",
75
+ ],
76
+ "Hash": [
77
+ "blake3",
78
+ "hmacSha256",
79
+ "keccak256",
80
+ "ripemd160",
81
+ "sha256",
82
+ ],
83
+ "Keystore": [
84
+ "pbkdf2Sha256",
85
+ ],
86
+ "Mnemonic": [
87
+ "toSeed",
88
+ ],
89
+ "X25519": [
90
+ "getPublicKey",
91
+ "getSharedSecret",
92
+ ],
93
+ }
94
+ `)
61
95
  })
62
96
  })
@@ -1,10 +1,11 @@
1
+ import { blake3 } from '@noble/hashes/blake3.js'
1
2
  import { keccak_256 } from '@noble/hashes/sha3.js'
2
3
  import { describe, expect, test, vi } from 'vp/test'
3
4
  import * as Engine from '../../core/Engine.js'
4
5
  import * as Hash from '../../core/Hash.js'
5
6
  import * as WasmHash from '../Hash.js'
6
7
 
7
- describe('create', () => {
8
+ describe('engine', () => {
8
9
  test('behavior: instantiates asynchronously', async () => {
9
10
  // Chrome refuses to compile modules above a few kilobytes synchronously on
10
11
  // the main thread, so `new WebAssembly.Module` must never be reached. A spy
@@ -13,12 +14,13 @@ describe('create', () => {
13
14
  const Module = vi.spyOn(globalThis.WebAssembly, 'Module')
14
15
  const Instance = vi.spyOn(globalThis.WebAssembly, 'Instance')
15
16
  try {
16
- const engine = await WasmHash.create()
17
+ const engine = await WasmHash.engine()
17
18
  expect(Module).not.toHaveBeenCalled()
18
19
  expect(Instance).not.toHaveBeenCalled()
19
20
 
20
21
  const input = new Uint8Array(32).fill(1)
21
- expect(engine.Hash.keccak256(input)).toEqual(keccak_256(input))
22
+ expect(engine.keccak256(input)).toEqual(keccak_256(input))
23
+ expect(engine.blake3(input)).toEqual(blake3(input))
22
24
  } finally {
23
25
  Module.mockRestore()
24
26
  Instance.mockRestore()
@@ -26,20 +28,25 @@ describe('create', () => {
26
28
  })
27
29
 
28
30
  test('behavior: grows memory in a real browser, then hashes a small input', async () => {
29
- const engine = await WasmHash.create()
31
+ const engine = await WasmHash.engine()
30
32
 
31
33
  const large = new Uint8Array(3 * 1024 * 1024).fill(7)
32
- expect(engine.Hash.keccak256(large)).toEqual(keccak_256(large))
34
+ expect(engine.keccak256(large)).toEqual(keccak_256(large))
35
+ expect(engine.blake3(large)).toEqual(blake3(large))
33
36
 
34
37
  // `memory.grow` detached the previous `ArrayBuffer`; a retained view would
35
38
  // now read zero bytes.
36
39
  const small = new Uint8Array(32).fill(1)
37
- expect(engine.Hash.keccak256(small)).toEqual(keccak_256(small))
40
+ expect(engine.keccak256(small)).toEqual(keccak_256(small))
41
+ expect(engine.blake3(small)).toEqual(blake3(small))
38
42
  })
39
43
 
40
44
  test('behavior: ox uses the WASM implementation once installed', async () => {
41
- Engine.set(await WasmHash.create())
45
+ await Engine.install({ Hash: WasmHash.engine() })
42
46
  try {
47
+ expect(Hash.blake3('0xdeadbeef')).toBe(
48
+ '0x53147f3ce49ed4f60dfa5b9654c36ba6103c11f5737df3dabd4cbd296c4161bd',
49
+ )
43
50
  expect(Hash.keccak256('0xdeadbeef')).toBe(
44
51
  '0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1',
45
52
  )
@@ -1,4 +1,5 @@
1
1
  import { fc, test } from '@fast-check/vitest'
2
+ import { blake3 } from '@noble/hashes/blake3.js'
2
3
  import { hmac } from '@noble/hashes/hmac.js'
3
4
  import { ripemd160 } from '@noble/hashes/legacy.js'
4
5
  import { sha256 } from '@noble/hashes/sha2.js'
@@ -7,10 +8,10 @@ import { Hash as WasmHash } from 'ox/wasm'
7
8
  import { beforeAll, describe, expect } from 'vp/test'
8
9
  import { numRuns } from '../../../test/fuzz/numRuns.js'
9
10
 
10
- let engine: WasmHash.create.ReturnType
11
+ let engine: WasmHash.engine.ReturnType
11
12
 
12
13
  beforeAll(async () => {
13
- engine = await WasmHash.create()
14
+ engine = await WasmHash.engine()
14
15
  })
15
16
 
16
17
  /**
@@ -21,14 +22,33 @@ beforeAll(async () => {
21
22
  *
22
23
  * The distribution is weighted toward the sizes where padding and block-boundary
23
24
  * bugs hide -- empty input, exact multiples of the keccak256 rate (136) and the
24
- * SHA-2 block (64), and the 56-byte threshold at which the length counter no
25
- * longer fits in the final block.
25
+ * SHA-2 block (64), BLAKE3 chunk (1024), and the 56-byte threshold at which
26
+ * the SHA-2 length counter no longer fits in the final block.
26
27
  */
27
28
  const arbitraryInput = fc.oneof(
28
29
  { arbitrary: fc.uint8Array({ maxLength: 300, minLength: 0 }), weight: 6 },
29
30
  {
30
31
  arbitrary: fc
31
- .constantFrom(0, 1, 55, 56, 63, 64, 65, 111, 112, 135, 136, 137, 272)
32
+ .constantFrom(
33
+ 0,
34
+ 1,
35
+ 55,
36
+ 56,
37
+ 63,
38
+ 64,
39
+ 65,
40
+ 111,
41
+ 112,
42
+ 135,
43
+ 136,
44
+ 137,
45
+ 272,
46
+ 1023,
47
+ 1024,
48
+ 1025,
49
+ 2048,
50
+ 2049,
51
+ )
32
52
  .chain((size) => fc.uint8Array({ maxLength: size, minLength: size })),
33
53
  weight: 3,
34
54
  },
@@ -54,55 +74,63 @@ const arbitraryKey = fc.oneof(
54
74
  )
55
75
 
56
76
  const primitives = {
77
+ blake3: {
78
+ reference: (input: Uint8Array) => blake3(input),
79
+ wasm: (input: Uint8Array) => engine.blake3(input),
80
+ },
57
81
  hmacSha256: {
58
82
  reference: (input: Uint8Array, key: Uint8Array) => hmac(sha256, key, input),
59
- wasm: (input: Uint8Array, key: Uint8Array) =>
60
- engine.Hash.hmacSha256(key, input),
83
+ wasm: (input: Uint8Array, key: Uint8Array) => engine.hmacSha256(key, input),
61
84
  },
62
85
  keccak256: {
63
86
  reference: (input: Uint8Array) => keccak_256(input),
64
- wasm: (input: Uint8Array) => engine.Hash.keccak256(input),
87
+ wasm: (input: Uint8Array) => engine.keccak256(input),
65
88
  },
66
89
  ripemd160: {
67
90
  reference: (input: Uint8Array) => ripemd160(input),
68
- wasm: (input: Uint8Array) => engine.Hash.ripemd160(input),
91
+ wasm: (input: Uint8Array) => engine.ripemd160(input),
69
92
  },
70
93
  sha256: {
71
94
  reference: (input: Uint8Array) => sha256(input),
72
- wasm: (input: Uint8Array) => engine.Hash.sha256(input),
95
+ wasm: (input: Uint8Array) => engine.sha256(input),
73
96
  },
74
97
  } as const
75
98
 
76
99
  type Name = keyof typeof primitives
77
100
 
78
101
  describe('Hash', () => {
102
+ test.prop({ input: arbitraryInput }, { numRuns })(
103
+ 'blake3 ≡ @noble/hashes',
104
+ ({ input }) => {
105
+ expect(engine.blake3(input)).toEqual(blake3(input))
106
+ },
107
+ )
108
+
79
109
  test.prop({ input: arbitraryInput }, { numRuns })(
80
110
  'keccak256 ≡ @noble/hashes',
81
111
  ({ input }) => {
82
- expect(engine.Hash.keccak256(input)).toEqual(keccak_256(input))
112
+ expect(engine.keccak256(input)).toEqual(keccak_256(input))
83
113
  },
84
114
  )
85
115
 
86
116
  test.prop({ input: arbitraryInput }, { numRuns })(
87
117
  'sha256 ≡ @noble/hashes',
88
118
  ({ input }) => {
89
- expect(engine.Hash.sha256(input)).toEqual(sha256(input))
119
+ expect(engine.sha256(input)).toEqual(sha256(input))
90
120
  },
91
121
  )
92
122
 
93
123
  test.prop({ input: arbitraryInput }, { numRuns })(
94
124
  'ripemd160 ≡ @noble/hashes',
95
125
  ({ input }) => {
96
- expect(engine.Hash.ripemd160(input)).toEqual(ripemd160(input))
126
+ expect(engine.ripemd160(input)).toEqual(ripemd160(input))
97
127
  },
98
128
  )
99
129
 
100
130
  test.prop({ input: arbitraryInput, key: arbitraryKey }, { numRuns })(
101
131
  'hmacSha256 ≡ @noble/hashes',
102
132
  ({ input, key }) => {
103
- expect(engine.Hash.hmacSha256(key, input)).toEqual(
104
- hmac(sha256, key, input),
105
- )
133
+ expect(engine.hmacSha256(key, input)).toEqual(hmac(sha256, key, input))
106
134
  },
107
135
  )
108
136
  })
@@ -115,6 +143,7 @@ describe('memory', () => {
115
143
  input: arbitraryInput,
116
144
  key: arbitraryKey,
117
145
  name: fc.constantFrom<Name>(
146
+ 'blake3',
118
147
  'hmacSha256',
119
148
  'keccak256',
120
149
  'ripemd160',
@@ -156,7 +185,7 @@ describe('memory', () => {
156
185
  // as zeroes rather than throwing.
157
186
  for (const pages of sizes) {
158
187
  const input = new Uint8Array(pages * 65_536).fill(pages & 0xff)
159
- expect(engine.Hash.keccak256(input), `${pages} pages`).toEqual(
188
+ expect(engine.keccak256(input), `${pages} pages`).toEqual(
160
189
  keccak_256(input),
161
190
  )
162
191
  }
@@ -167,10 +196,15 @@ describe('memory', () => {
167
196
  ({ input }) => {
168
197
  // A digest returned as a view over linear memory would be silently
169
198
  // rewritten by the next call. Hold one across a larger call and check it.
170
- const digest = engine.Hash.keccak256(input)
199
+ const digest = engine.keccak256(input)
171
200
  const snapshot = digest.slice()
172
- engine.Hash.keccak256(new Uint8Array(input.length + 1024).fill(0xaa))
201
+ engine.keccak256(new Uint8Array(input.length + 1024).fill(0xaa))
173
202
  expect(digest).toEqual(snapshot)
203
+
204
+ const blake3Digest = engine.blake3(input)
205
+ const blake3Snapshot = blake3Digest.slice()
206
+ engine.blake3(new Uint8Array(input.length + 1024).fill(0xaa))
207
+ expect(blake3Digest).toEqual(blake3Snapshot)
174
208
  },
175
209
  )
176
210
 
@@ -178,10 +212,11 @@ describe('memory', () => {
178
212
  'inputs are not mutated',
179
213
  ({ input }) => {
180
214
  const snapshot = input.slice()
181
- engine.Hash.keccak256(input)
182
- engine.Hash.sha256(input)
183
- engine.Hash.ripemd160(input)
184
- engine.Hash.hmacSha256(input, input)
215
+ engine.blake3(input)
216
+ engine.keccak256(input)
217
+ engine.sha256(input)
218
+ engine.ripemd160(input)
219
+ engine.hmacSha256(input, input)
185
220
  expect(input).toEqual(snapshot)
186
221
  },
187
222
  )
@@ -1,28 +1,34 @@
1
- import type { Engine } from 'ox'
2
1
  import { Hash as WasmHash } from 'ox/wasm'
3
2
  import { expectTypeOf, test } from 'vp/test'
3
+ import type * as CoreEngine from '../../core/Engine.js'
4
+ import * as CoreHash from '../../core/Hash.js'
4
5
 
5
- type Created = Awaited<ReturnType<typeof WasmHash.create>>
6
+ type Slot = Awaited<ReturnType<typeof WasmHash.engine>>
6
7
 
7
8
  test('every primitive is present, so callers need no assertion', () => {
8
- expectTypeOf<Created['Hash']['keccak256']>().toEqualTypeOf<
9
+ expectTypeOf<Slot['blake3']>().toEqualTypeOf<
9
10
  (input: Uint8Array) => Uint8Array
10
11
  >()
11
- expectTypeOf<Created['Hash']['sha256']>().toEqualTypeOf<
12
+ expectTypeOf<Slot['keccak256']>().toEqualTypeOf<
12
13
  (input: Uint8Array) => Uint8Array
13
14
  >()
14
- expectTypeOf<Created['Hash']['ripemd160']>().toEqualTypeOf<
15
+ expectTypeOf<Slot['sha256']>().toEqualTypeOf<
15
16
  (input: Uint8Array) => Uint8Array
16
17
  >()
17
- expectTypeOf<Created['Hash']['hmacSha256']>().toEqualTypeOf<
18
+ expectTypeOf<Slot['ripemd160']>().toEqualTypeOf<
19
+ (input: Uint8Array) => Uint8Array
20
+ >()
21
+ expectTypeOf<Slot['hmacSha256']>().toEqualTypeOf<
18
22
  (key: Uint8Array, message: Uint8Array) => Uint8Array
19
23
  >()
20
24
  })
21
25
 
22
- test('blake3 is absent, because the WASM module does not implement it', () => {
23
- expectTypeOf<Created['Hash']>().not.toHaveProperty('blake3')
26
+ test('the result is the raw slot, so `Engine.install` accepts it', () => {
27
+ expectTypeOf<{ Hash: Slot }>().toExtend<CoreEngine.Engine>()
24
28
  })
25
29
 
26
- test('the result is still an engine, so `Engine.set` accepts it', () => {
27
- expectTypeOf<Created>().toExtend<Engine.Engine>()
30
+ test('the WASM namespace exposes the public Hash API', () => {
31
+ expectTypeOf(WasmHash.blake3).toEqualTypeOf(CoreHash.blake3)
32
+ expectTypeOf(WasmHash.sha256).toEqualTypeOf(CoreHash.sha256)
33
+ expectTypeOf<typeof WasmHash>().not.toHaveProperty('create')
28
34
  })