ox 1.1.2 → 1.3.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 (296) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/core/AbiFunction.d.ts +1 -1
  3. package/dist/core/AbiFunction.d.ts.map +1 -1
  4. package/dist/core/AbiFunction.js +6 -2
  5. package/dist/core/AbiFunction.js.map +1 -1
  6. package/dist/core/AbiParameters.d.ts +1 -1
  7. package/dist/core/AbiParameters.d.ts.map +1 -1
  8. package/dist/core/AbiParameters.js +1 -13
  9. package/dist/core/AbiParameters.js.map +1 -1
  10. package/dist/core/AesGcm.d.ts +36 -1
  11. package/dist/core/AesGcm.d.ts.map +1 -1
  12. package/dist/core/AesGcm.js +44 -0
  13. package/dist/core/AesGcm.js.map +1 -1
  14. package/dist/core/Ed25519.d.ts +46 -1
  15. package/dist/core/Ed25519.d.ts.map +1 -1
  16. package/dist/core/Ed25519.js +43 -0
  17. package/dist/core/Ed25519.js.map +1 -1
  18. package/dist/core/Engine.d.ts +1 -1
  19. package/dist/core/Engine.d.ts.map +1 -1
  20. package/dist/core/Engine.js.map +1 -1
  21. package/dist/core/Hash.d.ts +200 -1
  22. package/dist/core/Hash.d.ts.map +1 -1
  23. package/dist/core/Hash.js +207 -0
  24. package/dist/core/Hash.js.map +1 -1
  25. package/dist/core/Hex.d.ts.map +1 -1
  26. package/dist/core/Hex.js +1 -4
  27. package/dist/core/Hex.js.map +1 -1
  28. package/dist/core/Rlp.d.ts +32 -0
  29. package/dist/core/Rlp.d.ts.map +1 -1
  30. package/dist/core/Rlp.js +146 -0
  31. package/dist/core/Rlp.js.map +1 -1
  32. package/dist/core/Secp256k1.d.ts +46 -1
  33. package/dist/core/Secp256k1.d.ts.map +1 -1
  34. package/dist/core/Secp256k1.js +48 -0
  35. package/dist/core/Secp256k1.js.map +1 -1
  36. package/dist/core/WebAuthn.d.ts +224 -0
  37. package/dist/core/WebAuthn.d.ts.map +1 -0
  38. package/dist/core/WebAuthn.js +343 -0
  39. package/dist/core/WebAuthn.js.map +1 -0
  40. package/dist/core/WebAuthnP256.d.ts +46 -50
  41. package/dist/core/WebAuthnP256.d.ts.map +1 -1
  42. package/dist/core/WebAuthnP256.js +44 -50
  43. package/dist/core/WebAuthnP256.js.map +1 -1
  44. package/dist/core/internal/abiParameters.d.ts +7 -87
  45. package/dist/core/internal/abiParameters.d.ts.map +1 -1
  46. package/dist/core/internal/abiParameters.js +339 -194
  47. package/dist/core/internal/abiParameters.js.map +1 -1
  48. package/dist/core/internal/engine.d.ts +35 -2
  49. package/dist/core/internal/engine.d.ts.map +1 -1
  50. package/dist/core/internal/engine.js +12 -1
  51. package/dist/core/internal/engine.js.map +1 -1
  52. package/dist/core/internal/hash.d.ts +10 -0
  53. package/dist/core/internal/hash.d.ts.map +1 -1
  54. package/dist/core/internal/hash.js +26 -1
  55. package/dist/core/internal/hash.js.map +1 -1
  56. package/dist/index.d.ts +23 -14
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +23 -14
  59. package/dist/index.js.map +1 -1
  60. package/dist/node/Engine.d.ts +1 -1
  61. package/dist/node/Engine.d.ts.map +1 -1
  62. package/dist/node/Engine.js.map +1 -1
  63. package/dist/node/Hash.d.ts +6 -4
  64. package/dist/node/Hash.d.ts.map +1 -1
  65. package/dist/node/Hash.js +35 -3
  66. package/dist/node/Hash.js.map +1 -1
  67. package/dist/tempo/AuthorizationTempo.d.ts +6 -6
  68. package/dist/tempo/AuthorizationTempo.js +6 -6
  69. package/dist/tempo/KeyAuthorization.d.ts +3 -8
  70. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  71. package/dist/tempo/KeyAuthorization.js +3 -8
  72. package/dist/tempo/KeyAuthorization.js.map +1 -1
  73. package/dist/tempo/SignatureEnvelope.d.ts +8 -8
  74. package/dist/tempo/SignatureEnvelope.js +8 -8
  75. package/dist/trusted-setups/Paths.d.ts +1 -0
  76. package/dist/trusted-setups/Paths.d.ts.map +1 -1
  77. package/dist/trusted-setups/Paths.js +1 -0
  78. package/dist/trusted-setups/Paths.js.map +1 -1
  79. package/dist/trusted-setups/Setups.d.ts +35 -0
  80. package/dist/trusted-setups/Setups.d.ts.map +1 -0
  81. package/dist/trusted-setups/Setups.js +29 -0
  82. package/dist/trusted-setups/Setups.js.map +1 -0
  83. package/dist/trusted-setups/index.d.ts +20 -0
  84. package/dist/trusted-setups/index.d.ts.map +1 -1
  85. package/dist/trusted-setups/index.js +20 -0
  86. package/dist/trusted-setups/index.js.map +1 -1
  87. package/dist/trusted-setups/internal/paths.d.ts.map +1 -1
  88. package/dist/trusted-setups/internal/paths.js +1 -1
  89. package/dist/trusted-setups/internal/paths.js.map +1 -1
  90. package/dist/trusted-setups/internal/setups/mainnet.d.ts +7 -0
  91. package/dist/trusted-setups/internal/setups/mainnet.d.ts.map +1 -0
  92. package/dist/trusted-setups/internal/setups/mainnet.js +11 -0
  93. package/dist/trusted-setups/internal/setups/mainnet.js.map +1 -0
  94. package/dist/wasm/Engine.d.ts +4 -3
  95. package/dist/wasm/Engine.d.ts.map +1 -1
  96. package/dist/wasm/Engine.js +5 -5
  97. package/dist/wasm/Engine.js.map +1 -1
  98. package/dist/wasm/Hash.d.ts +1 -1
  99. package/dist/wasm/Hash.d.ts.map +1 -1
  100. package/dist/wasm/Hash.js +5 -0
  101. package/dist/wasm/Hash.js.map +1 -1
  102. package/dist/wasm/Keystore.d.ts +23 -9
  103. package/dist/wasm/Keystore.d.ts.map +1 -1
  104. package/dist/wasm/Keystore.js +30 -13
  105. package/dist/wasm/Keystore.js.map +1 -1
  106. package/dist/wasm/Kzg.d.ts +112 -0
  107. package/dist/wasm/Kzg.d.ts.map +1 -0
  108. package/dist/wasm/Kzg.js +333 -0
  109. package/dist/wasm/Kzg.js.map +1 -0
  110. package/dist/wasm/Secp256k1.d.ts +55 -0
  111. package/dist/wasm/Secp256k1.d.ts.map +1 -0
  112. package/dist/wasm/Secp256k1.js +181 -0
  113. package/dist/wasm/Secp256k1.js.map +1 -0
  114. package/dist/wasm/index.d.ts +44 -1
  115. package/dist/wasm/index.d.ts.map +1 -1
  116. package/dist/wasm/index.js +44 -1
  117. package/dist/wasm/index.js.map +1 -1
  118. package/dist/wasm/internal/blake3.d.ts +10 -1
  119. package/dist/wasm/internal/blake3.d.ts.map +1 -1
  120. package/dist/wasm/internal/blake3.js +12 -1
  121. package/dist/wasm/internal/blake3.js.map +1 -1
  122. package/dist/wasm/internal/blake3.wasm.d.ts +3 -1
  123. package/dist/wasm/internal/blake3.wasm.d.ts.map +1 -1
  124. package/dist/wasm/internal/blake3.wasm.js +3 -1
  125. package/dist/wasm/internal/blake3.wasm.js.map +1 -1
  126. package/dist/wasm/internal/hash.d.ts +9 -0
  127. package/dist/wasm/internal/hash.d.ts.map +1 -1
  128. package/dist/wasm/internal/hash.js +13 -1
  129. package/dist/wasm/internal/hash.js.map +1 -1
  130. package/dist/wasm/internal/hashState.d.ts +25 -0
  131. package/dist/wasm/internal/hashState.d.ts.map +1 -0
  132. package/dist/wasm/internal/hashState.js +105 -0
  133. package/dist/wasm/internal/hashState.js.map +1 -0
  134. package/dist/wasm/internal/hashes.d.ts +12 -1
  135. package/dist/wasm/internal/hashes.d.ts.map +1 -1
  136. package/dist/wasm/internal/hashes.js +18 -1
  137. package/dist/wasm/internal/hashes.js.map +1 -1
  138. package/dist/wasm/internal/hashes.wasm.d.ts +11 -3
  139. package/dist/wasm/internal/hashes.wasm.d.ts.map +1 -1
  140. package/dist/wasm/internal/hashes.wasm.js +11 -3
  141. package/dist/wasm/internal/hashes.wasm.js.map +1 -1
  142. package/dist/wasm/internal/instantiate.d.ts +12 -0
  143. package/dist/wasm/internal/instantiate.d.ts.map +1 -1
  144. package/dist/wasm/internal/instantiate.js +23 -7
  145. package/dist/wasm/internal/instantiate.js.map +1 -1
  146. package/dist/wasm/internal/keystore.d.ts +10 -0
  147. package/dist/wasm/internal/keystore.d.ts.map +1 -0
  148. package/dist/wasm/internal/keystore.js +10 -0
  149. package/dist/wasm/internal/keystore.js.map +1 -0
  150. package/dist/wasm/internal/kzg.d.ts +17 -0
  151. package/dist/wasm/internal/kzg.d.ts.map +1 -0
  152. package/dist/wasm/internal/kzg.js +8 -0
  153. package/dist/wasm/internal/kzg.js.map +1 -0
  154. package/dist/wasm/internal/kzg.wasm.d.ts +3 -0
  155. package/dist/wasm/internal/kzg.wasm.d.ts.map +1 -0
  156. package/dist/wasm/internal/kzg.wasm.js +10 -0
  157. package/dist/wasm/internal/kzg.wasm.js.map +1 -0
  158. package/dist/wasm/internal/scrypt.d.ts +16 -0
  159. package/dist/wasm/internal/scrypt.d.ts.map +1 -0
  160. package/dist/wasm/internal/scrypt.js +78 -0
  161. package/dist/wasm/internal/scrypt.js.map +1 -0
  162. package/dist/wasm/internal/scrypt.wasm.d.ts +7 -0
  163. package/dist/wasm/internal/scrypt.wasm.d.ts.map +1 -0
  164. package/dist/wasm/internal/scrypt.wasm.js +14 -0
  165. package/dist/wasm/internal/scrypt.wasm.js.map +1 -0
  166. package/dist/wasm/internal/secp256k1.d.ts +16 -0
  167. package/dist/wasm/internal/secp256k1.d.ts.map +1 -0
  168. package/dist/wasm/internal/secp256k1.js +20 -0
  169. package/dist/wasm/internal/secp256k1.js.map +1 -0
  170. package/dist/wasm/internal/secp256k1.wasm.d.ts +3 -0
  171. package/dist/wasm/internal/secp256k1.wasm.d.ts.map +1 -0
  172. package/dist/wasm/internal/secp256k1.wasm.js +10 -0
  173. package/dist/wasm/internal/secp256k1.wasm.js.map +1 -0
  174. package/dist/webauthn/Authentication.d.ts +2 -0
  175. package/dist/webauthn/Authentication.d.ts.map +1 -1
  176. package/dist/webauthn/Authentication.js +2 -1
  177. package/dist/webauthn/Authentication.js.map +1 -1
  178. package/dist/webauthn/Registration.d.ts.map +1 -1
  179. package/dist/webauthn/Registration.js +2 -1
  180. package/dist/webauthn/Registration.js.map +1 -1
  181. package/dist/webauthn/Types.d.ts +27 -6
  182. package/dist/webauthn/Types.d.ts.map +1 -1
  183. package/dist/webauthn/internal/utils.d.ts.map +1 -1
  184. package/dist/webauthn/internal/utils.js +35 -9
  185. package/dist/webauthn/internal/utils.js.map +1 -1
  186. package/dist/zod/TypedData.d.ts +2 -2
  187. package/package.json +27 -2
  188. package/src/benchmarks/Ox.bench.ts +6 -0
  189. package/src/benchmarks/OxNode.bench.ts +8 -0
  190. package/src/benchmarks/OxV0.bench.ts +4 -0
  191. package/src/benchmarks/OxWasm.bench.ts +22 -0
  192. package/src/core/AbiFunction.ts +5 -5
  193. package/src/core/AbiParameters.ts +8 -20
  194. package/src/core/AesGcm.ts +86 -1
  195. package/src/core/Ed25519.ts +87 -1
  196. package/src/core/Engine.ts +1 -0
  197. package/src/core/Hash.ts +321 -1
  198. package/src/core/Hex.ts +1 -4
  199. package/src/core/Rlp.ts +172 -5
  200. package/src/core/Secp256k1.ts +92 -1
  201. package/src/core/WebAuthn.ts +579 -0
  202. package/src/core/WebAuthnP256.ts +47 -50
  203. package/src/core/_test/AbiFunction.test.ts +9 -0
  204. package/src/core/_test/AbiParameters.differential.fuzz.ts +156 -0
  205. package/src/core/_test/AbiParameters.encode.test.ts +22 -0
  206. package/src/core/_test/AbiParameters.fuzz.ts +4 -0
  207. package/src/core/_test/AesGcm.test.ts +76 -0
  208. package/src/core/_test/Block.test.ts +9 -2
  209. package/src/core/_test/Ed25519.test-d.ts +13 -1
  210. package/src/core/_test/Ed25519.test.ts +120 -1
  211. package/src/core/_test/Engine.test-d.ts +9 -0
  212. package/src/core/_test/Engine.test.ts +1 -1
  213. package/src/core/_test/Hash.test-d.ts +41 -0
  214. package/src/core/_test/Hash.test.ts +278 -1
  215. package/src/core/_test/Hash.vectors.test.ts +1 -2
  216. package/src/core/_test/Hex.test.ts +2 -0
  217. package/src/core/_test/Rlp.test-d.ts +18 -0
  218. package/src/core/_test/Rlp.test.ts +109 -0
  219. package/src/core/_test/Secp256k1.test-d.ts +14 -0
  220. package/src/core/_test/Secp256k1.test.ts +91 -1
  221. package/src/core/_test/Transaction.test.ts +11 -2
  222. package/src/core/_test/WebAuthn.test-d.ts +142 -0
  223. package/src/core/_test/WebAuthn.test.ts +766 -0
  224. package/src/core/_test/index.test.ts +1 -0
  225. package/src/core/internal/abiParameters.ts +466 -326
  226. package/src/core/internal/engine.ts +47 -2
  227. package/src/core/internal/hash.ts +53 -1
  228. package/src/index.ts +24 -14
  229. package/src/node/Engine.ts +1 -0
  230. package/src/node/Hash.ts +43 -6
  231. package/src/node/_test/Engine.test-d.ts +1 -0
  232. package/src/node/_test/Engine.test.ts +2 -0
  233. package/src/node/_test/Hash.test-d.ts +11 -0
  234. package/src/node/_test/Hash.test.ts +46 -0
  235. package/src/tempo/AuthorizationTempo.ts +6 -6
  236. package/src/tempo/KeyAuthorization.ts +3 -8
  237. package/src/tempo/SignatureEnvelope.ts +8 -8
  238. package/src/trusted-setups/Paths.ts +1 -0
  239. package/src/trusted-setups/README.md +39 -0
  240. package/src/trusted-setups/Setups.ts +46 -0
  241. package/src/trusted-setups/_test/Paths.test.ts +16 -0
  242. package/src/trusted-setups/_test/Setups.test-d.ts +7 -0
  243. package/src/trusted-setups/_test/Setups.test.ts +43 -0
  244. package/src/trusted-setups/_test/index.test.ts +1 -0
  245. package/src/trusted-setups/index.ts +21 -0
  246. package/src/trusted-setups/internal/paths.ts +4 -1
  247. package/src/trusted-setups/internal/setups/mainnet.source.json +7 -0
  248. package/src/trusted-setups/internal/setups/mainnet.ts +16 -0
  249. package/src/version.ts +1 -1
  250. package/src/wasm/Engine.ts +9 -6
  251. package/src/wasm/Hash.ts +10 -0
  252. package/src/wasm/Keystore.bench.ts +26 -0
  253. package/src/wasm/Keystore.ts +32 -14
  254. package/src/wasm/Kzg.bench.ts +63 -0
  255. package/src/wasm/Kzg.ts +518 -0
  256. package/src/wasm/Secp256k1.bench.ts +79 -0
  257. package/src/wasm/Secp256k1.ts +248 -0
  258. package/src/wasm/_test/Engine.test-d.ts +2 -0
  259. package/src/wasm/_test/Engine.test.ts +5 -0
  260. package/src/wasm/_test/Hash.browser.test.ts +31 -0
  261. package/src/wasm/_test/Hash.test-d.ts +17 -0
  262. package/src/wasm/_test/Hash.test.ts +252 -3
  263. package/src/wasm/_test/Keystore.browser.test.ts +31 -0
  264. package/src/wasm/_test/Keystore.fuzz.ts +50 -0
  265. package/src/wasm/_test/Keystore.test-d.ts +10 -1
  266. package/src/wasm/_test/Keystore.test.ts +289 -4
  267. package/src/wasm/_test/Kzg.test-d.ts +19 -0
  268. package/src/wasm/_test/Kzg.test.ts +220 -0
  269. package/src/wasm/_test/Runtime.test.ts +9 -0
  270. package/src/wasm/_test/Secp256k1.test-d.ts +35 -0
  271. package/src/wasm/_test/Secp256k1.test.ts +354 -0
  272. package/src/wasm/index.ts +46 -1
  273. package/src/wasm/internal/blake3.ts +23 -2
  274. package/src/wasm/internal/blake3.wasm.ts +4 -1
  275. package/src/wasm/internal/hash.ts +27 -1
  276. package/src/wasm/internal/hashState.ts +154 -0
  277. package/src/wasm/internal/hashes.ts +56 -15
  278. package/src/wasm/internal/hashes.wasm.ts +15 -3
  279. package/src/wasm/internal/instantiate.ts +29 -9
  280. package/src/wasm/internal/keystore.ts +19 -0
  281. package/src/wasm/internal/kzg.ts +50 -0
  282. package/src/wasm/internal/kzg.wasm.ts +11 -0
  283. package/src/wasm/internal/scrypt.ts +131 -0
  284. package/src/wasm/internal/scrypt.wasm.ts +17 -0
  285. package/src/wasm/internal/secp256k1.ts +62 -0
  286. package/src/wasm/internal/secp256k1.wasm.ts +11 -0
  287. package/src/webauthn/Authentication.ts +4 -0
  288. package/src/webauthn/Registration.ts +2 -0
  289. package/src/webauthn/Types.ts +32 -2
  290. package/src/webauthn/_test/Authentication.test.ts +123 -1
  291. package/src/webauthn/_test/Registration.test.ts +26 -6
  292. package/src/webauthn/_test/WebAuthn.browser.test.ts +28 -0
  293. package/src/webauthn/internal/utils.ts +41 -8
  294. package/wasm/vendor/c-kzg-4844/LICENSE +201 -0
  295. package/wasm/vendor/c-kzg-4844/blst/LICENSE +201 -0
  296. package/src/trusted-setups/internal/setups/mainnet.txt +0 -4163
@@ -0,0 +1,220 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from 'vp/test'
2
+ import { kzg as reference } from '../../../test/kzg.js'
3
+ import type * as Bytes from '../../core/Bytes.js'
4
+ import type * as CoreKzg from '../../core/Kzg.js'
5
+ import { mainnet as trustedSetup } from '../../trusted-setups/Setups.js'
6
+ import * as Kzg from '../Kzg.js'
7
+
8
+ const blob = Uint8Array.from({ length: 131_072 }, (_, index) =>
9
+ index % 32 === 31 ? index % 251 : 0,
10
+ )
11
+
12
+ let instance: Kzg.create.ReturnType
13
+ let commitment: Bytes.Bytes
14
+ let cells: readonly Bytes.Bytes[]
15
+ let proofs: readonly Bytes.Bytes[]
16
+
17
+ beforeAll(async () => {
18
+ instance = await Kzg.create({ trustedSetup })
19
+ commitment = instance.blobToKzgCommitment(blob)
20
+ ;({ cells, proofs } = instance.computeCellsAndKzgProofs(blob))
21
+ }, 120_000)
22
+
23
+ afterAll(() => {
24
+ instance.dispose()
25
+ })
26
+
27
+ describe('create', () => {
28
+ test('returns the injected Kzg.Kzg boundary', () => {
29
+ expect(instance satisfies CoreKzg.Kzg).toMatchInlineSnapshot(`
30
+ {
31
+ "blobToKzgCommitment": [Function],
32
+ "computeCells": [Function],
33
+ "computeCellsAndKzgProofs": [Function],
34
+ "dispose": [Function],
35
+ "recoverCellsAndKzgProofs": [Function],
36
+ "verifyCellKzgProofBatch": [Function],
37
+ }
38
+ `)
39
+ })
40
+
41
+ test('compiles concurrently into independently owned instances', async () => {
42
+ const [first, second] = await Promise.all([
43
+ Kzg.create({ trustedSetup }),
44
+ Kzg.create({ trustedSetup }),
45
+ ])
46
+
47
+ first.dispose()
48
+ expect(() => first.blobToKzgCommitment(blob)).toThrow(Kzg.DisposedError)
49
+ expect(second.blobToKzgCommitment(blob)).toEqual(commitment)
50
+ second.dispose()
51
+ second.dispose()
52
+ expect(() => second.computeCells(blob)).toThrowErrorMatchingInlineSnapshot(
53
+ `[Kzg.DisposedError: KZG instance has been disposed.]`,
54
+ )
55
+ }, 120_000)
56
+
57
+ test('rejects precomputation beyond the memory bound', async () => {
58
+ await expect(
59
+ Kzg.create({ precompute: 9, trustedSetup }),
60
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
61
+ `[Kzg.InvalidPrecomputeError: \`precompute\` must be an integer from 0 through 8. Got 9.]`,
62
+ )
63
+ })
64
+
65
+ test('rejects malformed trusted setup points', async () => {
66
+ await expect(
67
+ Kzg.create({
68
+ trustedSetup: {
69
+ ...trustedSetup,
70
+ g2_monomial: trustedSetup.g2_monomial.slice(1),
71
+ },
72
+ }),
73
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
74
+ `[Kzg.InvalidTrustedSetupError: \`g2_monomial\` must contain exactly 6240 bytes.]`,
75
+ )
76
+ })
77
+
78
+ test('rejects invalid trusted setup points', async () => {
79
+ const g1Lagrange = trustedSetup.g1_lagrange.slice()
80
+ g1Lagrange.fill(0, 0, 48)
81
+ await expect(
82
+ Kzg.create({
83
+ trustedSetup: {
84
+ ...trustedSetup,
85
+ g1_lagrange: g1Lagrange,
86
+ },
87
+ }),
88
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
89
+ `[Kzg.InvalidTrustedSetupError: c-kzg rejected the trusted setup.]`,
90
+ )
91
+ }, 120_000)
92
+ })
93
+
94
+ describe('blobToKzgCommitment', () => {
95
+ test('conformance: matches the independent reference implementation', () => {
96
+ expect(commitment).toEqual(reference.blobToKzgCommitment(blob))
97
+ })
98
+
99
+ test('rejects a malformed blob', () => {
100
+ expect(() =>
101
+ instance.blobToKzgCommitment(new Uint8Array(31)),
102
+ ).toThrowErrorMatchingInlineSnapshot(
103
+ `[Kzg.InvalidInputError: \`blob\` must contain exactly 131072 bytes.]`,
104
+ )
105
+ })
106
+
107
+ test('reuses setup and scratch state', () => {
108
+ for (let i = 0; i < 100; i++)
109
+ expect(instance.blobToKzgCommitment(blob)).toEqual(commitment)
110
+ })
111
+ })
112
+
113
+ describe('computeCells', () => {
114
+ test('conformance: matches the independent reference implementation', () => {
115
+ expect(instance.computeCells(blob)).toEqual(reference.computeCells(blob))
116
+ }, 120_000)
117
+ })
118
+
119
+ describe('computeCellsAndKzgProofs', () => {
120
+ test('conformance: matches the independent reference implementation', () => {
121
+ const expected = reference.computeCellsAndKzgProofs(blob)
122
+ expect(cells).toEqual(expected.cells)
123
+ expect(proofs).toEqual(expected.proofs)
124
+ }, 120_000)
125
+ })
126
+
127
+ describe('recoverCellsAndKzgProofs', () => {
128
+ test('conformance: matches the independent reference implementation', () => {
129
+ const indices = cells.map((_, index) => index).filter((index) => index % 2)
130
+ const partial = cells.filter((_, index) => index % 2)
131
+ const expected = reference.recoverCellsAndKzgProofs(indices, partial)
132
+
133
+ expect(instance.recoverCellsAndKzgProofs(indices, partial)).toEqual(
134
+ expected,
135
+ )
136
+ }, 120_000)
137
+
138
+ test('accepts unordered indices', () => {
139
+ const indices = cells
140
+ .map((_, index) => index)
141
+ .filter((index) => index % 2)
142
+ .reverse()
143
+ const partial = indices.map((index) => cells[index]!)
144
+
145
+ expect(instance.recoverCellsAndKzgProofs(indices, partial)).toEqual({
146
+ cells,
147
+ proofs,
148
+ })
149
+ }, 120_000)
150
+
151
+ test('rejects duplicate indices', () => {
152
+ expect(() =>
153
+ instance.recoverCellsAndKzgProofs(
154
+ Array.from({ length: 64 }, () => 0),
155
+ cells.slice(0, 64),
156
+ ),
157
+ ).toThrowErrorMatchingInlineSnapshot(
158
+ `[Kzg.InvalidInputError: \`cellIndices\` must not contain duplicate indices.]`,
159
+ )
160
+ })
161
+ })
162
+
163
+ describe('verifyCellKzgProofBatch', () => {
164
+ const indices = Array.from({ length: 128 }, (_, index) => index)
165
+
166
+ test('conformance: matches the independent reference implementation', () => {
167
+ const commitments = cells.map(() => commitment)
168
+ expect(
169
+ instance.verifyCellKzgProofBatch(commitments, indices, cells, proofs),
170
+ ).toBe(
171
+ reference.verifyCellKzgProofBatch(commitments, indices, cells, proofs),
172
+ )
173
+ }, 120_000)
174
+
175
+ test('returns false for a corrupted cell', () => {
176
+ const corrupted = cells.map((cell, index) => {
177
+ if (index !== 7) return cell
178
+ const value = Uint8Array.from(cell)
179
+ value[0] = value[0]! ^ 1
180
+ return value
181
+ })
182
+ expect(
183
+ instance.verifyCellKzgProofBatch(
184
+ cells.map(() => commitment),
185
+ indices,
186
+ corrupted,
187
+ proofs,
188
+ ),
189
+ ).toMatchInlineSnapshot('false')
190
+ })
191
+
192
+ test('rejects mismatched input lengths', () => {
193
+ expect(() =>
194
+ instance.verifyCellKzgProofBatch(
195
+ [commitment],
196
+ [],
197
+ [cells[0]!],
198
+ [proofs[0]!],
199
+ ),
200
+ ).toThrowErrorMatchingInlineSnapshot(
201
+ `[Kzg.InvalidInputError: \`commitments\`, \`cellIndices\`, \`cells\`, and \`proofs\` must have the same non-zero length.]`,
202
+ )
203
+ })
204
+ })
205
+
206
+ test('exports', () => {
207
+ expect(Object.keys(Kzg)).toMatchInlineSnapshot(`
208
+ [
209
+ "create",
210
+ "DisposedError",
211
+ "InvalidInputError",
212
+ "InvalidPrecomputeError",
213
+ "InvalidTrustedSetupError",
214
+ "MemoryError",
215
+ "WasmError",
216
+ "versionedHashVersion",
217
+ "from",
218
+ ]
219
+ `)
220
+ })
@@ -19,3 +19,12 @@ test('memmove compares flat-memory offsets rather than C object pointers', () =>
19
19
  expect(body).toContain('if ((size_t)d < (size_t)s)')
20
20
  expect(body).not.toMatch(/if\s*\(\s*d\s*<\s*s\s*\)/)
21
21
  })
22
+
23
+ test('KZG artifact loads only when create runs', () => {
24
+ const source = readFileSync(new URL('../Kzg.ts', import.meta.url), 'utf8')
25
+
26
+ expect(source).not.toMatch(
27
+ /^import(?!\s+type\b)[^\n]*['"]\.\/internal\/kzg\.js['"]/m,
28
+ )
29
+ expect(source).toContain("await import('./internal/kzg.js')")
30
+ })
@@ -0,0 +1,35 @@
1
+ import { expectTypeOf, test } from 'vp/test'
2
+ import type * as Engine from '../../core/Engine.js'
3
+ import * as core_Secp256k1 from '../../core/Secp256k1.js'
4
+ import * as Secp256k1 from '../Secp256k1.js'
5
+
6
+ type Slot = Awaited<ReturnType<typeof Secp256k1.engine>>
7
+
8
+ test('return type exposes only deterministic Secp256k1 primitives', () => {
9
+ expectTypeOf<Slot['getPublicKey']>().toEqualTypeOf<
10
+ NonNullable<Engine.Ecdsa['getPublicKey']>
11
+ >()
12
+ expectTypeOf<Slot['getSharedSecret']>().toEqualTypeOf<
13
+ NonNullable<Engine.Ecdsa['getSharedSecret']>
14
+ >()
15
+ expectTypeOf<Slot['recoverPublicKey']>().toEqualTypeOf<
16
+ NonNullable<Engine.Ecdsa['recoverPublicKey']>
17
+ >()
18
+ expectTypeOf<Slot['sign']>().toEqualTypeOf<
19
+ NonNullable<Engine.Ecdsa['sign']>
20
+ >()
21
+ expectTypeOf<Slot['verify']>().toEqualTypeOf<
22
+ NonNullable<Engine.Ecdsa['verify']>
23
+ >()
24
+ expectTypeOf<Slot>().not.toHaveProperty('randomSecretKey')
25
+ })
26
+
27
+ test('the WASM namespace exposes the public Secp256k1 API', () => {
28
+ expectTypeOf(Secp256k1.getPublicKey).toEqualTypeOf(
29
+ core_Secp256k1.getPublicKey,
30
+ )
31
+ expectTypeOf(Secp256k1.randomPrivateKey).toEqualTypeOf(
32
+ core_Secp256k1.randomPrivateKey,
33
+ )
34
+ expectTypeOf<typeof Secp256k1>().not.toHaveProperty('create')
35
+ })
@@ -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
  *