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
package/src/core/Hash.ts CHANGED
@@ -3,6 +3,65 @@ import type * as Errors from './Errors.js'
3
3
  import * as Hex from './Hex.js'
4
4
  import * as engine from './internal/hash.js'
5
5
 
6
+ /**
7
+ * Calculates the [BLAKE3](https://github.com/BLAKE3-team/BLAKE3) hash of a {@link ox#Bytes.Bytes} or {@link ox#Hex.Hex} value.
8
+ *
9
+ * Backed by `blake3` from [`@noble/hashes`](https://github.com/paulmillr/noble-hashes), an audited & minimal JS hashing library, unless another implementation is installed with {@link ox#Engine.set}.
10
+ *
11
+ * @example
12
+ * ```ts twoslash
13
+ * import { Hash } from 'ox'
14
+ *
15
+ * Hash.blake3('0xdeadbeef')
16
+ * // @log: '0x53147f3ce49ed4f60dfa5b9654c36ba6103c11f5737df3dabd4cbd296c4161bd'
17
+ * ```
18
+ *
19
+ * @example
20
+ * ### Configure Return Type
21
+ *
22
+ * ```ts twoslash
23
+ * import { Hash } from 'ox'
24
+ *
25
+ * Hash.blake3('0xdeadbeef', { as: 'Bytes' })
26
+ * // @log: Uint8Array [...]
27
+ * ```
28
+ *
29
+ * @param value - {@link ox#Bytes.Bytes} or {@link ox#Hex.Hex} value.
30
+ * @param options - Options.
31
+ * @returns BLAKE3 hash.
32
+ */
33
+ export function blake3<
34
+ value extends Hex.Hex | Bytes.Bytes,
35
+ as extends 'Hex' | 'Bytes' =
36
+ | (value extends Hex.Hex ? 'Hex' : never)
37
+ | (value extends Bytes.Bytes ? 'Bytes' : never),
38
+ >(
39
+ value: value | Hex.Hex | Bytes.Bytes,
40
+ options: blake3.Options<as> = {},
41
+ ): blake3.ReturnType<as> {
42
+ const isBytes = value instanceof Uint8Array
43
+ const { as = isBytes ? 'Bytes' : 'Hex' } = options
44
+ const bytes = engine.blake3(isBytes ? value : Bytes.from(value))
45
+ if (as === 'Bytes') return bytes as never
46
+ return Hex.fromBytes(bytes) as never
47
+ }
48
+
49
+ export declare namespace blake3 {
50
+ type Options<as extends 'Hex' | 'Bytes' = 'Hex' | 'Bytes'> = {
51
+ /** The return type. Defaults to the input format. */
52
+ as?: as | 'Hex' | 'Bytes' | undefined
53
+ }
54
+
55
+ type ReturnType<as extends 'Hex' | 'Bytes' = 'Hex' | 'Bytes'> =
56
+ | (as extends 'Bytes' ? Bytes.Bytes : never)
57
+ | (as extends 'Hex' ? Hex.Hex : never)
58
+
59
+ type ErrorType =
60
+ | Bytes.from.ErrorType
61
+ | Hex.fromBytes.ErrorType
62
+ | Errors.GlobalErrorType
63
+ }
64
+
6
65
  /**
7
66
  * Calculates the [Keccak256](https://en.wikipedia.org/wiki/SHA-3) hash of a {@link ox#Bytes.Bytes} or {@link ox#Hex.Hex} value.
8
67
  *
@@ -400,9 +400,16 @@ describe('fromRpc', () => {
400
400
  method: 'eth_getBlockByNumber',
401
401
  params: ['latest', true],
402
402
  })
403
- .then(Block.fromRpc)
403
+ .then((block) => Block.fromRpc(block, { includeTransactions: true }))
404
404
 
405
- expect(block).toMatchSnapshot()
405
+ const transactions = block?.transactions.map((transaction) => {
406
+ const { blockTimestamp: _, ...transaction_ } = {
407
+ blockTimestamp: undefined,
408
+ ...transaction,
409
+ }
410
+ return transaction_
411
+ })
412
+ expect({ ...block, transactions }).toMatchSnapshot()
406
413
  })
407
414
  })
408
415
 
@@ -17,6 +17,62 @@ type Unlisted = {
17
17
  >
18
18
  }[keyof Engine.Engine]
19
19
 
20
+ describe('install', () => {
21
+ test('accepts raw and promised slots and preserves their resolved shape', () => {
22
+ const keccak256 = (input: Uint8Array) => input
23
+ const toSeed = (_mnemonic: string) => new Uint8Array(64)
24
+
25
+ expectTypeOf(
26
+ Engine.install({
27
+ Hash: Promise.resolve({ keccak256 }),
28
+ Mnemonic: { toSeed },
29
+ }),
30
+ ).toEqualTypeOf<
31
+ Promise<{
32
+ Hash: { keccak256: typeof keccak256 }
33
+ Mnemonic: { toSeed: typeof toSeed }
34
+ }>
35
+ >()
36
+ })
37
+
38
+ test('rejects an unknown slot', () => {
39
+ // @ts-expect-error
40
+ void Engine.install({ Keccak: Promise.resolve({}) })
41
+ })
42
+
43
+ test('rejects an unknown slot alongside a valid slot', () => {
44
+ void Engine.install({
45
+ Hash: { keccak256: (input) => input },
46
+ // @ts-expect-error
47
+ Keccak: {},
48
+ })
49
+ })
50
+
51
+ test('rejects a misspelled function', () => {
52
+ void Engine.install({
53
+ // @ts-expect-error
54
+ Hash: Promise.resolve({ keccak_256: (input: Uint8Array) => input }),
55
+ })
56
+ })
57
+
58
+ test('rejects a misspelled function alongside a valid function', () => {
59
+ void Engine.install({
60
+ // @ts-expect-error
61
+ Hash: {
62
+ keccak256: (input: Uint8Array) => input,
63
+ keccak_256: (input: Uint8Array) => input,
64
+ },
65
+ })
66
+ })
67
+
68
+ test('rejects a mistyped promised function', () => {
69
+ void Engine.install({
70
+ // @ts-expect-error
71
+ Hash: Promise.resolve({ keccak256: (input: string) => input }),
72
+ })
73
+ })
74
+ })
75
+
20
76
  describe('set', () => {
21
77
  test('accepts a partial engine', () => {
22
78
  expectTypeOf(Engine.set).parameter(0).toEqualTypeOf<Engine.Engine>()
@@ -38,6 +38,101 @@ function merkelize() {
38
38
  return BinaryStateTree.merkelize(tree)
39
39
  }
40
40
 
41
+ describe('install', () => {
42
+ test('behavior: resolves slots in parallel and installs them atomically', async () => {
43
+ const keccak256 = () => new Uint8Array(32).fill(1)
44
+ const sign = () => new Uint8Array(65).fill(3)
45
+ const toSeed = () => new Uint8Array(64).fill(2)
46
+ let releaseHash: (() => void) | undefined
47
+
48
+ Engine.set({ Secp256k1: { sign } })
49
+
50
+ const hash = {
51
+ // oxlint-disable-next-line unicorn/no-thenable -- Distinguishes parallel resolution from sequential awaits.
52
+ then(
53
+ resolve: (value: Engine.Hash) => void,
54
+ reject: (reason: unknown) => void,
55
+ ) {
56
+ const timeout = setTimeout(
57
+ () => reject(new Error('slots resolved serially')),
58
+ 0,
59
+ )
60
+ releaseHash = () => {
61
+ clearTimeout(timeout)
62
+ resolve({ keccak256 })
63
+ }
64
+ },
65
+ } as unknown as PromiseLike<Engine.Hash>
66
+ const mnemonic = {
67
+ // oxlint-disable-next-line unicorn/no-thenable -- Distinguishes parallel resolution from sequential awaits.
68
+ then(resolve: (value: Engine.Mnemonic) => void) {
69
+ resolve({ toSeed })
70
+ releaseHash?.()
71
+ },
72
+ } as unknown as PromiseLike<Engine.Mnemonic>
73
+
74
+ const installed = await Engine.install({ Hash: hash, Mnemonic: mnemonic })
75
+
76
+ expect(installed).toEqual({
77
+ Hash: { keccak256 },
78
+ Mnemonic: { toSeed },
79
+ })
80
+ expect(Engine.get()).toEqual({
81
+ ...installed,
82
+ Secp256k1: { sign },
83
+ })
84
+ })
85
+
86
+ test('behavior: a rejected slot leaves the previous engine installed', async () => {
87
+ const keccak256 = () => new Uint8Array(32).fill(1)
88
+ Engine.set({ Hash: { keccak256 } })
89
+
90
+ await expect(
91
+ Engine.install({
92
+ Hash: Promise.resolve({
93
+ sha256: () => new Uint8Array(32).fill(2),
94
+ }),
95
+ Mnemonic: Promise.reject(new Error('could not load engine')),
96
+ }),
97
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
98
+ `[Error: could not load engine]`,
99
+ )
100
+ expect(Engine.get()).toEqual({ Hash: { keccak256 } })
101
+ })
102
+
103
+ test('behavior: an invalid resolved engine leaves the previous engine installed', async () => {
104
+ const keccak256 = () => new Uint8Array(32).fill(1)
105
+ Engine.set({ Hash: { keccak256 } })
106
+
107
+ await expect(
108
+ Engine.install({
109
+ Hash: Promise.resolve({
110
+ sha256: () => new Uint8Array(32).fill(2),
111
+ }),
112
+ Mnemonic: Promise.resolve({
113
+ to_seed: () => new Uint8Array(64),
114
+ }),
115
+ } as never),
116
+ ).rejects.toThrowError(Engine.UnknownPrimitiveError)
117
+ expect(Engine.get()).toEqual({ Hash: { keccak256 } })
118
+ })
119
+
120
+ test('behavior: a null resolved slot leaves the previous engine installed', async () => {
121
+ const keccak256 = () => new Uint8Array(32).fill(1)
122
+ Engine.set({ Hash: { keccak256 } })
123
+
124
+ await expect(
125
+ Engine.install({
126
+ Hash: Promise.resolve({
127
+ sha256: () => new Uint8Array(32).fill(2),
128
+ }),
129
+ Mnemonic: Promise.resolve(null),
130
+ } as never),
131
+ ).rejects.toThrowError(Engine.InvalidSlotValueError)
132
+ expect(Engine.get()).toEqual({ Hash: { keccak256 } })
133
+ })
134
+ })
135
+
41
136
  describe('set', () => {
42
137
  test('behavior: empty engine is a no-op', () => {
43
138
  const before = Hash.keccak256(payload)
@@ -0,0 +1,18 @@
1
+ import { type Bytes, Hash, type Hex } from 'ox'
2
+ import { describe, expectTypeOf, test } from 'vp/test'
3
+
4
+ describe('blake3', () => {
5
+ test('default', () => {
6
+ expectTypeOf(Hash.blake3('0x')).toEqualTypeOf<Hex.Hex>()
7
+ expectTypeOf(Hash.blake3(new Uint8Array())).toEqualTypeOf<Bytes.Bytes>()
8
+ })
9
+
10
+ test('as', () => {
11
+ expectTypeOf(
12
+ Hash.blake3('0x', { as: 'Bytes' }),
13
+ ).toEqualTypeOf<Bytes.Bytes>()
14
+ expectTypeOf(
15
+ Hash.blake3(new Uint8Array(), { as: 'Hex' }),
16
+ ).toEqualTypeOf<Hex.Hex>()
17
+ })
18
+ })
@@ -1,6 +1,104 @@
1
- import { Hash } from 'ox'
1
+ import { Engine, Hash } from 'ox'
2
2
  import { describe, expect, test } from 'vp/test'
3
3
 
4
+ describe('blake3', () => {
5
+ test('default', () => {
6
+ expect(Hash.blake3('0x')).toMatchInlineSnapshot(
7
+ `"0xaf1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"`,
8
+ )
9
+
10
+ expect(Hash.blake3(new Uint8Array())).toMatchInlineSnapshot(`
11
+ Uint8Array [
12
+ 175,
13
+ 19,
14
+ 73,
15
+ 185,
16
+ 245,
17
+ 249,
18
+ 161,
19
+ 166,
20
+ 160,
21
+ 64,
22
+ 77,
23
+ 234,
24
+ 54,
25
+ 220,
26
+ 201,
27
+ 73,
28
+ 155,
29
+ 203,
30
+ 37,
31
+ 201,
32
+ 173,
33
+ 193,
34
+ 18,
35
+ 183,
36
+ 204,
37
+ 154,
38
+ 147,
39
+ 202,
40
+ 228,
41
+ 31,
42
+ 50,
43
+ 98,
44
+ ]
45
+ `)
46
+ })
47
+
48
+ test('as: Hex', () => {
49
+ expect(Hash.blake3(new Uint8Array(), { as: 'Hex' })).toMatchInlineSnapshot(
50
+ `"0xaf1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"`,
51
+ )
52
+ })
53
+
54
+ test('as: Bytes', () => {
55
+ expect(Hash.blake3('0x', { as: 'Bytes' })).toMatchInlineSnapshot(`
56
+ Uint8Array [
57
+ 175,
58
+ 19,
59
+ 73,
60
+ 185,
61
+ 245,
62
+ 249,
63
+ 161,
64
+ 166,
65
+ 160,
66
+ 64,
67
+ 77,
68
+ 234,
69
+ 54,
70
+ 220,
71
+ 201,
72
+ 73,
73
+ 155,
74
+ 203,
75
+ 37,
76
+ 201,
77
+ 173,
78
+ 193,
79
+ 18,
80
+ 183,
81
+ 204,
82
+ 154,
83
+ 147,
84
+ 202,
85
+ 228,
86
+ 31,
87
+ 50,
88
+ 98,
89
+ ]
90
+ `)
91
+ })
92
+
93
+ test('behavior: routes through the installed engine', () => {
94
+ Engine.set({ Hash: { blake3: () => new Uint8Array(32).fill(1) } })
95
+
96
+ expect(Hash.blake3('0x')).toMatchInlineSnapshot(
97
+ `"0x0101010101010101010101010101010101010101010101010101010101010101"`,
98
+ )
99
+ })
100
+ })
101
+
4
102
  describe('hmac256', () => {
5
103
  test('default', () => {
6
104
  expect(
@@ -530,6 +628,7 @@ describe('validate', () => {
530
628
  test('exports', () => {
531
629
  expect(Object.keys(Hash)).toMatchInlineSnapshot(`
532
630
  [
631
+ "blake3",
533
632
  "keccak256",
534
633
  "hmac256",
535
634
  "ripemd160",
@@ -11,6 +11,13 @@ import * as vectors from '../../../test/vectors/hashes/index.js'
11
11
  * See `test/vectors/hashes/README.md`.
12
12
  */
13
13
 
14
+ describe('blake3', () => {
15
+ test(`matches ${vectors.blake3.length} official BLAKE3 vectors`, () => {
16
+ for (const { digest, message } of vectors.blake3)
17
+ expect(Hash.blake3(message, { as: 'Bytes' })).toEqual(digest)
18
+ })
19
+ })
20
+
14
21
  describe('sha256', () => {
15
22
  test(`matches ${vectors.sha256.length} NIST CAVP vectors`, () => {
16
23
  for (const { digest, message } of vectors.sha256)
@@ -158,7 +158,11 @@ describe('fromRpc', () => {
158
158
  })
159
159
  .then(Transaction.fromRpc)
160
160
 
161
- expect(transaction).toMatchInlineSnapshot(`
161
+ const { blockTimestamp: _, ...transaction_ } = {
162
+ blockTimestamp: undefined,
163
+ ...transaction,
164
+ }
165
+ expect(transaction_).toMatchInlineSnapshot(`
162
166
  {
163
167
  "blockHash": "0x8299a290ae51a8958965987e2caf178a09b8ecde01b737b5f9a8badbacb407cb",
164
168
  "blockNumber": 1110749n,
@@ -411,7 +415,12 @@ describe('fromRpc', () => {
411
415
  ],
412
416
  })
413
417
  .then(Transaction.fromRpc)
414
- expect(transaction).toMatchInlineSnapshot(`
418
+
419
+ const { blockTimestamp: _, ...transaction_ } = {
420
+ blockTimestamp: undefined,
421
+ ...transaction,
422
+ }
423
+ expect(transaction_).toMatchInlineSnapshot(`
415
424
  {
416
425
  "accessList": [],
417
426
  "blockHash": "0xc350d807505fb835650f0013632c5515592987ba169bbc6626d9fc54d91f0f0b",
@@ -263,21 +263,21 @@ export type Bls = {
263
263
  * @internal
264
264
  */
265
265
  export type Engine = {
266
- /** Implementation for {@link ox#Bls}. */
266
+ /** Implementation for [`Bls`](/api/Bls). */
267
267
  Bls?: Bls | undefined
268
- /** Implementation for {@link ox#Ed25519}. */
268
+ /** Implementation for [`Ed25519`](/api/Ed25519). */
269
269
  Ed25519?: Eddsa | undefined
270
- /** Implementation for {@link ox#Hash}. */
270
+ /** Implementation for [`Hash`](/api/Hash). */
271
271
  Hash?: Hash | undefined
272
- /** Implementation for {@link ox#Keystore}. */
272
+ /** Implementation for [`Keystore`](/api/Keystore). */
273
273
  Keystore?: Keystore | undefined
274
- /** Implementation for {@link ox#Mnemonic}. */
274
+ /** Implementation for [`Mnemonic`](/api/Mnemonic). */
275
275
  Mnemonic?: Mnemonic | undefined
276
- /** Implementation for {@link ox#P256}. */
276
+ /** Implementation for [`P256`](/api/P256). */
277
277
  P256?: Ecdsa | undefined
278
- /** Implementation for {@link ox#Secp256k1}. */
278
+ /** Implementation for [`Secp256k1`](/api/Secp256k1). */
279
279
  Secp256k1?: Ecdsa | undefined
280
- /** Implementation for {@link ox#X25519}. */
280
+ /** Implementation for [`X25519`](/api/X25519). */
281
281
  X25519?: Ecdh | undefined
282
282
  }
283
283
 
package/src/index.ts CHANGED
@@ -1536,9 +1536,12 @@ export * as Ed25519 from './core/Ed25519.js'
1536
1536
  *
1537
1537
  * ```ts twoslash
1538
1538
  * // @noErrors
1539
- * import { Engine } from 'ox/wasm'
1539
+ * import { Engine } from 'ox'
1540
+ * import { Hash } from 'ox/wasm'
1540
1541
  *
1541
- * await Engine.load()
1542
+ * await Engine.install({
1543
+ * Hash: Hash.engine()
1544
+ * })
1542
1545
  * ```
1543
1546
  *
1544
1547
  * @example
@@ -0,0 +1,87 @@
1
+ import * as crypto from 'node:crypto'
2
+ import type * as Engine from '../core/Engine.js'
3
+ import type * as Errors from '../core/Errors.js'
4
+
5
+ export * from '../core/Ed25519.js'
6
+
7
+ const privateKeyPrefix = Buffer.from('302e020100300506032b657004220420', 'hex')
8
+
9
+ /**
10
+ * Creates a Node.js implementation of the [`Ed25519`](/api/Ed25519) engine
11
+ * slot, without installing it.
12
+ *
13
+ * Node's verifier does not implement Ox's ZIP-215 verification semantics, so
14
+ * this engine deliberately omits `verify`. Public-key conversion and random
15
+ * key generation also remain on Ox's default implementation.
16
+ *
17
+ * @example
18
+ * ```ts twoslash
19
+ * // @noErrors
20
+ * import { Engine } from 'ox'
21
+ * import { Ed25519 } from 'ox/node'
22
+ *
23
+ * await Engine.install({ Ed25519: Ed25519.engine() })
24
+ *
25
+ * Ed25519.getPublicKey({ privateKey: '0x...' })
26
+ * ```
27
+ *
28
+ * @returns The raw `Ed25519` engine slot.
29
+ */
30
+ export function engine(): Promise<engine.ReturnType> {
31
+ return Promise.resolve({
32
+ getPublicKey: (privateKey) =>
33
+ rawPublicKey(crypto.createPublicKey(toPrivateKey(privateKey))),
34
+ sign: (payload, privateKey) =>
35
+ new Uint8Array(crypto.sign(null, payload, toPrivateKey(privateKey))),
36
+ toMontgomerySecret: (privateKey) => {
37
+ assertLength(privateKey)
38
+ const digest = crypto.hash('sha512', privateKey, 'buffer')
39
+ try {
40
+ const secretKey = Uint8Array.from(digest.subarray(0, 32))
41
+ secretKey[0]! &= 248
42
+ secretKey[31]! &= 127
43
+ secretKey[31]! |= 64
44
+ return secretKey
45
+ } finally {
46
+ digest.fill(0)
47
+ }
48
+ },
49
+ })
50
+ }
51
+
52
+ export declare namespace engine {
53
+ /** Every `Ed25519` primitive this module implements. */
54
+ type ReturnType = {
55
+ [key in 'getPublicKey' | 'sign' | 'toMontgomerySecret']-?: NonNullable<
56
+ Engine.Eddsa[key]
57
+ >
58
+ }
59
+
60
+ type ErrorType = Errors.GlobalErrorType
61
+ }
62
+
63
+ function assertLength(key: Uint8Array): void {
64
+ if (key.length !== 32)
65
+ throw new RangeError(
66
+ `Ed25519 private key must be 32 bytes, got ${key.length}`,
67
+ )
68
+ }
69
+
70
+ function rawPublicKey(publicKey: crypto.KeyObject): Uint8Array {
71
+ const der = publicKey.export({ format: 'der', type: 'spki' })
72
+ return Uint8Array.from(der.subarray(-32))
73
+ }
74
+
75
+ function toPrivateKey(privateKey: Uint8Array): crypto.KeyObject {
76
+ assertLength(privateKey)
77
+ const der = Buffer.concat([privateKeyPrefix, privateKey])
78
+ try {
79
+ return crypto.createPrivateKey({
80
+ format: 'der',
81
+ key: der,
82
+ type: 'pkcs8',
83
+ })
84
+ } finally {
85
+ der.fill(0)
86
+ }
87
+ }