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
package/src/core/Hash.ts CHANGED
@@ -1,8 +1,246 @@
1
1
  import * as Bytes from './Bytes.js'
2
- import type * as Errors from './Errors.js'
2
+ import * as Errors from './Errors.js'
3
3
  import * as Hex from './Hex.js'
4
+ import type { HashState } from './internal/engine.js'
4
5
  import * as engine from './internal/hash.js'
5
6
 
7
+ /**
8
+ * Incremental hash state.
9
+ *
10
+ * A hasher accepts any number of chunks. Calling `digest` or `digestInto`
11
+ * consumes the state. Call `clone` before finalizing to branch from the same
12
+ * prefix.
13
+ */
14
+ export type Hasher = {
15
+ /** Creates an independent copy of the current state. */
16
+ clone(): Hasher
17
+ /** Destroys the state. Calling `destroy` more than once has no effect. */
18
+ destroy(): void
19
+ /** Finalizes the digest and consumes the state. */
20
+ digest<as extends 'Hex' | 'Bytes' = 'Hex'>(
21
+ options?: Hasher.DigestOptions<as>,
22
+ ): Hasher.DigestReturnType<as>
23
+ /**
24
+ * Finalizes into the start of `output` and consumes the state.
25
+ *
26
+ * `output` may be larger than the digest. Bytes after the digest are left
27
+ * unchanged.
28
+ */
29
+ digestInto(output: Bytes.Bytes): void
30
+ /** Absorbs a message chunk. */
31
+ update(value: Hex.Hex | Bytes.Bytes): Hasher
32
+ }
33
+
34
+ export declare namespace Hasher {
35
+ /** Options for `Hasher.digest`. */
36
+ type DigestOptions<as extends 'Hex' | 'Bytes' = 'Hex'> = {
37
+ /** The return type. @default 'Hex' */
38
+ as?: as | 'Hex' | 'Bytes' | undefined
39
+ }
40
+
41
+ /** Return type for `Hasher.digest`. */
42
+ type DigestReturnType<as extends 'Hex' | 'Bytes' = 'Hex'> =
43
+ | (as extends 'Bytes' ? Bytes.Bytes : never)
44
+ | (as extends 'Hex' ? Hex.Hex : never)
45
+
46
+ /** Errors thrown by an incremental hash state. */
47
+ type ErrorType =
48
+ | HasherDestroyedError
49
+ | InvalidDigestSizeError
50
+ | Bytes.from.ErrorType
51
+ | Hex.fromBytes.ErrorType
52
+ | Errors.GlobalErrorType
53
+ }
54
+
55
+ /**
56
+ * Creates an incremental BLAKE3 hasher.
57
+ *
58
+ * The installed Engine provider is captured when this function is called.
59
+ *
60
+ * @example
61
+ * ```ts twoslash
62
+ * import { Hash } from 'ox'
63
+ *
64
+ * const hash = Hash.createBlake3()
65
+ * hash.update('0xdead')
66
+ * hash.update('0xbeef')
67
+ * hash.digest()
68
+ * // @log: '0x53147f3ce49ed4f60dfa5b9654c36ba6103c11f5737df3dabd4cbd296c4161bd'
69
+ * ```
70
+ *
71
+ * @returns An incremental BLAKE3 hasher.
72
+ */
73
+ export function createBlake3(): Hasher {
74
+ return fromState(engine.createBlake3(), 32)
75
+ }
76
+
77
+ export declare namespace createBlake3 {
78
+ type ErrorType = Hasher.ErrorType
79
+ }
80
+
81
+ /**
82
+ * Creates an incremental HMAC-SHA256 hasher.
83
+ *
84
+ * The installed Engine provider is captured when this function is called.
85
+ *
86
+ * @example
87
+ * ```ts twoslash
88
+ * import { Hash, Hex } from 'ox'
89
+ *
90
+ * const hash = Hash.createHmac256(Hex.fromString('key'))
91
+ * hash.update('0xdead')
92
+ * hash.update('0xbeef')
93
+ * hash.digest()
94
+ * ```
95
+ *
96
+ * @param key - HMAC key.
97
+ * @returns An incremental HMAC-SHA256 hasher.
98
+ */
99
+ export function createHmac256(key: Hex.Hex | Bytes.Bytes): Hasher {
100
+ return fromState(engine.createHmacSha256(Bytes.from(key)), 32)
101
+ }
102
+
103
+ export declare namespace createHmac256 {
104
+ type ErrorType = Hasher.ErrorType
105
+ }
106
+
107
+ /**
108
+ * Creates an incremental Keccak256 hasher.
109
+ *
110
+ * The installed Engine provider is captured when this function is called.
111
+ *
112
+ * @example
113
+ * ```ts twoslash
114
+ * import { Hash } from 'ox'
115
+ *
116
+ * const hash = Hash.createKeccak256()
117
+ * hash.update('0xdead')
118
+ * hash.update('0xbeef')
119
+ * hash.digest()
120
+ * // @log: '0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1'
121
+ * ```
122
+ *
123
+ * @returns An incremental Keccak256 hasher.
124
+ */
125
+ export function createKeccak256(): Hasher {
126
+ return fromState(engine.createKeccak256(), 32)
127
+ }
128
+
129
+ export declare namespace createKeccak256 {
130
+ type ErrorType = Hasher.ErrorType
131
+ }
132
+
133
+ /**
134
+ * Creates an incremental RIPEMD-160 hasher.
135
+ *
136
+ * The installed Engine provider is captured when this function is called.
137
+ *
138
+ * @example
139
+ * ```ts twoslash
140
+ * import { Hash } from 'ox'
141
+ *
142
+ * const hash = Hash.createRipemd160()
143
+ * hash.update('0xdead')
144
+ * hash.update('0xbeef')
145
+ * hash.digest()
146
+ * // @log: '0x226821c2f5423e11fe9af68bd285c249db2e4b5a'
147
+ * ```
148
+ *
149
+ * @returns An incremental RIPEMD-160 hasher.
150
+ */
151
+ export function createRipemd160(): Hasher {
152
+ return fromState(engine.createRipemd160(), 20)
153
+ }
154
+
155
+ export declare namespace createRipemd160 {
156
+ type ErrorType = Hasher.ErrorType
157
+ }
158
+
159
+ /**
160
+ * Creates an incremental SHA-256 hasher.
161
+ *
162
+ * The installed Engine provider is captured when this function is called.
163
+ *
164
+ * @example
165
+ * ```ts twoslash
166
+ * import { Hash } from 'ox'
167
+ *
168
+ * const hash = Hash.createSha256()
169
+ * hash.update('0xdead')
170
+ * hash.update('0xbeef')
171
+ * hash.digest()
172
+ * // @log: '0x5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953'
173
+ * ```
174
+ *
175
+ * @returns An incremental SHA-256 hasher.
176
+ */
177
+ export function createSha256(): Hasher {
178
+ return fromState(engine.createSha256(), 32)
179
+ }
180
+
181
+ export declare namespace createSha256 {
182
+ type ErrorType = Hasher.ErrorType
183
+ }
184
+
185
+ /**
186
+ * Calculates the [BLAKE3](https://github.com/BLAKE3-team/BLAKE3) hash of a {@link ox#Bytes.Bytes} or {@link ox#Hex.Hex} value.
187
+ *
188
+ * 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}.
189
+ *
190
+ * @example
191
+ * ```ts twoslash
192
+ * import { Hash } from 'ox'
193
+ *
194
+ * Hash.blake3('0xdeadbeef')
195
+ * // @log: '0x53147f3ce49ed4f60dfa5b9654c36ba6103c11f5737df3dabd4cbd296c4161bd'
196
+ * ```
197
+ *
198
+ * @example
199
+ * ### Configure Return Type
200
+ *
201
+ * ```ts twoslash
202
+ * import { Hash } from 'ox'
203
+ *
204
+ * Hash.blake3('0xdeadbeef', { as: 'Bytes' })
205
+ * // @log: Uint8Array [...]
206
+ * ```
207
+ *
208
+ * @param value - {@link ox#Bytes.Bytes} or {@link ox#Hex.Hex} value.
209
+ * @param options - Options.
210
+ * @returns BLAKE3 hash.
211
+ */
212
+ export function blake3<
213
+ value extends Hex.Hex | Bytes.Bytes,
214
+ as extends 'Hex' | 'Bytes' =
215
+ | (value extends Hex.Hex ? 'Hex' : never)
216
+ | (value extends Bytes.Bytes ? 'Bytes' : never),
217
+ >(
218
+ value: value | Hex.Hex | Bytes.Bytes,
219
+ options: blake3.Options<as> = {},
220
+ ): blake3.ReturnType<as> {
221
+ const isBytes = value instanceof Uint8Array
222
+ const { as = isBytes ? 'Bytes' : 'Hex' } = options
223
+ const bytes = engine.blake3(isBytes ? value : Bytes.from(value))
224
+ if (as === 'Bytes') return bytes as never
225
+ return Hex.fromBytes(bytes) as never
226
+ }
227
+
228
+ export declare namespace blake3 {
229
+ type Options<as extends 'Hex' | 'Bytes' = 'Hex' | 'Bytes'> = {
230
+ /** The return type. Defaults to the input format. */
231
+ as?: as | 'Hex' | 'Bytes' | undefined
232
+ }
233
+
234
+ type ReturnType<as extends 'Hex' | 'Bytes' = 'Hex' | 'Bytes'> =
235
+ | (as extends 'Bytes' ? Bytes.Bytes : never)
236
+ | (as extends 'Hex' ? Hex.Hex : never)
237
+
238
+ type ErrorType =
239
+ | Bytes.from.ErrorType
240
+ | Hex.fromBytes.ErrorType
241
+ | Errors.GlobalErrorType
242
+ }
243
+
6
244
  /**
7
245
  * Calculates the [Keccak256](https://en.wikipedia.org/wiki/SHA-3) hash of a {@link ox#Bytes.Bytes} or {@link ox#Hex.Hex} value.
8
246
  *
@@ -264,3 +502,85 @@ export declare namespace validate {
264
502
  | Hex.size.ErrorType
265
503
  | Errors.GlobalErrorType
266
504
  }
505
+
506
+ /** Thrown when an incremental hash state has been consumed or destroyed. */
507
+ export class HasherDestroyedError extends Errors.BaseError {
508
+ override readonly name = 'Hash.HasherDestroyedError'
509
+
510
+ constructor() {
511
+ super('Hasher has been destroyed.')
512
+ }
513
+ }
514
+
515
+ /** Thrown when a digest output buffer is too small. */
516
+ export class InvalidDigestSizeError extends Errors.BaseError {
517
+ override readonly name = 'Hash.InvalidDigestSizeError'
518
+
519
+ constructor(options: InvalidDigestSizeError.Options) {
520
+ const { minimum, size } = options
521
+ super(
522
+ `Digest output is too small. Expected at least ${minimum} bytes. Received ${size} bytes.`,
523
+ )
524
+ }
525
+ }
526
+
527
+ export declare namespace InvalidDigestSizeError {
528
+ type Options = {
529
+ /** Minimum output size. */
530
+ minimum: number
531
+ /** Received output size. */
532
+ size: number
533
+ }
534
+ }
535
+
536
+ function fromState(state: HashState, digestSize: number): Hasher {
537
+ let active = true
538
+
539
+ const assertActive = () => {
540
+ if (!active) throw new HasherDestroyedError()
541
+ }
542
+
543
+ const consume = <returnType>(fn: () => returnType): returnType => {
544
+ assertActive()
545
+ active = false
546
+ try {
547
+ return fn()
548
+ } finally {
549
+ state.destroy()
550
+ }
551
+ }
552
+
553
+ const hasher: Hasher = {
554
+ clone() {
555
+ assertActive()
556
+ return fromState(state.clone(), digestSize)
557
+ },
558
+ destroy() {
559
+ if (!active) return
560
+ active = false
561
+ state.destroy()
562
+ },
563
+ digest(options = {}) {
564
+ const value = new Uint8Array(digestSize)
565
+ consume(() => state.digestInto(value))
566
+ if (options.as === 'Bytes') return value as never
567
+ return Hex.fromBytes(value) as never
568
+ },
569
+ digestInto(output) {
570
+ assertActive()
571
+ if (output.length < digestSize)
572
+ throw new InvalidDigestSizeError({
573
+ minimum: digestSize,
574
+ size: output.length,
575
+ })
576
+ consume(() => state.digestInto(output))
577
+ },
578
+ update(value) {
579
+ assertActive()
580
+ state.update(Bytes.from(value))
581
+ return hasher
582
+ },
583
+ }
584
+
585
+ return hasher
586
+ }
package/src/core/Hex.ts CHANGED
@@ -74,10 +74,7 @@ export function concat(...values: readonly Hex[]): Hex {
74
74
  if (values.length === 1) return values[0]!
75
75
  if (values.length === 2)
76
76
  return `0x${(values[0] as string).slice(2)}${(values[1] as string).slice(2)}` as Hex
77
- const parts = Array.from<string>({ length: values.length })
78
- for (let i = 0; i < values.length; i++)
79
- parts[i] = (values[i] as string).slice(2)
80
- return `0x${parts.join('')}` as Hex
77
+ return `0x${values.reduce((hex, value) => hex + value.replace('0x', ''), '')}`
81
78
  }
82
79
 
83
80
  export declare namespace concat {
package/src/core/Rlp.ts CHANGED
@@ -192,6 +192,48 @@ export declare namespace readList {
192
192
  | Errors.GlobalErrorType
193
193
  }
194
194
 
195
+ /**
196
+ * Encodes a value as Recursive-Length Prefix (RLP) and writes the encoded bytes
197
+ * through a synchronous callback without allocating the complete encoding.
198
+ *
199
+ * Ox validates the complete input before the first write. If the callback
200
+ * throws, the error propagates and earlier callback side effects remain.
201
+ *
202
+ * Ox does not mutate a chunk after the callback returns. A chunk may alias a
203
+ * byte-array leaf from the encoded value. The callback must not mutate the
204
+ * chunk before `encodeTo` returns.
205
+ *
206
+ * @example
207
+ * ```ts twoslash
208
+ * import { Bytes, Rlp } from 'ox'
209
+ *
210
+ * const chunks: Uint8Array[] = []
211
+ * Rlp.encodeTo(['0x01', '0x0203'], (chunk) => {
212
+ * chunks.push(chunk)
213
+ * })
214
+ *
215
+ * const encoded = Bytes.concat(...chunks)
216
+ * // @log: Uint8Array([196, 1, 130, 2, 3])
217
+ * ```
218
+ *
219
+ * @param value - The bytes or Hex value, or nested list of values, to encode.
220
+ * @param write - The synchronous callback for encoded chunks.
221
+ * @returns Nothing.
222
+ */
223
+ export function encodeTo(
224
+ value: RecursiveArray<Bytes.Bytes> | RecursiveArray<Hex.Hex>,
225
+ write: (chunk: Bytes.Bytes) => undefined,
226
+ ): void {
227
+ const snapshot = snapshotValue(value)
228
+ const ctx: EncodeCtx = { lengths: [], cursor: 0 }
229
+ measure(snapshot, ctx)
230
+ writeEncodedTo(write, snapshot, { lengths: ctx.lengths, cursor: 0 })
231
+ }
232
+
233
+ export declare namespace encodeTo {
234
+ type ErrorType = Errors.BaseError | Errors.GlobalErrorType
235
+ }
236
+
195
237
  /**
196
238
  * Encodes a {@link ox#Bytes.Bytes} or {@link ox#Hex.Hex} value into a Recursive-Length Prefix (RLP) value.
197
239
  *
@@ -368,7 +410,29 @@ function getSizeOfLength(length: number) {
368
410
  *
369
411
  * @internal
370
412
  */
371
- type EncodeCtx = { lengths: number[]; cursor: number }
413
+ type EncodeCtx = {
414
+ cursor: number
415
+ lengths: number[]
416
+ }
417
+
418
+ type Encodable = RecursiveArray<Bytes.Bytes | Hex.Hex>
419
+
420
+ /**
421
+ * Validates Hex leaves and copies list structure before the callback can mutate
422
+ * the input during encoding. Byte leaves remain zero-copy.
423
+ *
424
+ * @internal
425
+ */
426
+ function snapshotValue(value: Encodable): Encodable {
427
+ if (Array.isArray(value)) {
428
+ const snapshot: Encodable[] = []
429
+ for (let i = 0; i < value.length; i++)
430
+ snapshot.push(snapshotValue(value[i]!))
431
+ return snapshot
432
+ }
433
+ if (typeof value === 'string') validateHexLeaf(value)
434
+ return value
435
+ }
372
436
 
373
437
  /**
374
438
  * Walks `value` once, caches each list's `bodyLength` into `ctx.lengths`,
@@ -377,10 +441,7 @@ type EncodeCtx = { lengths: number[]; cursor: number }
377
441
  *
378
442
  * @internal
379
443
  */
380
- function measure(
381
- value: RecursiveArray<Bytes.Bytes> | RecursiveArray<Hex.Hex>,
382
- ctx: EncodeCtx,
383
- ): number {
444
+ function measure(value: Encodable, ctx: EncodeCtx): number {
384
445
  if (Array.isArray(value)) {
385
446
  // Reserve this list's slot before descending so children's slots come
386
447
  // after ours; `writeEncoded` walks in the same order and reads slot N
@@ -458,6 +519,112 @@ function writeEncoded(
458
519
  return writeBytesLeaf(bytes, offset, value as Bytes.Bytes)
459
520
  }
460
521
 
522
+ const writeChunkSize = 16_384
523
+
524
+ type Write = (chunk: Bytes.Bytes) => undefined
525
+
526
+ /**
527
+ * Writes an RLP value using list lengths cached by `measure`.
528
+ *
529
+ * @internal
530
+ */
531
+ function writeEncodedTo(write: Write, value: Encodable, ctx: EncodeCtx): void {
532
+ if (Array.isArray(value)) {
533
+ write(encodePrefix(ctx.lengths[ctx.cursor++]!, 0xc0))
534
+ for (let i = 0; i < value.length; i++) writeEncodedTo(write, value[i]!, ctx)
535
+ return
536
+ }
537
+
538
+ if (typeof value === 'string') {
539
+ writeHexLeafTo(write, value)
540
+ return
541
+ }
542
+
543
+ writeBytesLeafTo(write, value as Bytes.Bytes)
544
+ }
545
+
546
+ /**
547
+ * Writes a Hex leaf in bounded chunks. `snapshotValue` validated every nibble
548
+ * before this function can run.
549
+ *
550
+ * @internal
551
+ */
552
+ function writeHexLeafTo(write: Write, hex: Hex.Hex): void {
553
+ const byteLength = (hex.length - 1) >> 1
554
+ if (byteLength === 1 && readHexByte(hex, 0) < 0x80) {
555
+ write(Uint8Array.of(readHexByte(hex, 0)))
556
+ return
557
+ }
558
+
559
+ write(encodePrefix(byteLength, 0x80))
560
+ for (let offset = 0; offset < byteLength; offset += writeChunkSize) {
561
+ const length = Math.min(writeChunkSize, byteLength - offset)
562
+ const chunk = new Uint8Array(length)
563
+ for (let i = 0; i < length; i++) chunk[i] = readHexByte(hex, offset + i)
564
+ write(chunk)
565
+ }
566
+ }
567
+
568
+ /**
569
+ * Writes a byte leaf without copying its body.
570
+ *
571
+ * @internal
572
+ */
573
+ function writeBytesLeafTo(write: Write, leaf: Bytes.Bytes): void {
574
+ const length = leaf.length
575
+ if (length === 1 && leaf[0]! < 0x80) {
576
+ write(leaf)
577
+ return
578
+ }
579
+
580
+ write(encodePrefix(length, 0x80))
581
+ if (length > 0) write(leaf)
582
+ }
583
+
584
+ /**
585
+ * Encodes a string or list length prefix.
586
+ *
587
+ * @internal
588
+ */
589
+ function encodePrefix(length: number, offset: 0x80 | 0xc0): Uint8Array {
590
+ if (length <= 55) return Uint8Array.of(offset + length)
591
+
592
+ const size = getSizeOfLength(length)
593
+ const prefix = new Uint8Array(size + 1)
594
+ prefix[0] = offset + 55 + size
595
+ writeBigEndian(prefix, 1, length, size)
596
+ return prefix
597
+ }
598
+
599
+ /**
600
+ * Reads one byte from an odd- or even-nibble Hex value.
601
+ *
602
+ * @internal
603
+ */
604
+ function readHexByte(hex: Hex.Hex, index: number): number {
605
+ const odd = (hex.length & 1) === 1
606
+ if (odd && index === 0)
607
+ return internal_bytes.charCodeToBase16(hex.charCodeAt(2))!
608
+
609
+ const nibble = odd ? index * 2 - 1 : index * 2
610
+ const high = internal_bytes.charCodeToBase16(hex.charCodeAt(2 + nibble))!
611
+ const low = internal_bytes.charCodeToBase16(hex.charCodeAt(3 + nibble))!
612
+ return (high << 4) | low
613
+ }
614
+
615
+ /**
616
+ * Validates a Hex leaf while preserving RLP's odd-nibble left-padding.
617
+ *
618
+ * @internal
619
+ */
620
+ function validateHexLeaf(hex: string): asserts hex is Hex.Hex {
621
+ if (hex.length < 2 || hex.charCodeAt(0) !== 48 || hex.charCodeAt(1) !== 120)
622
+ throw invalidNibble(hex as Hex.Hex)
623
+ for (let i = 2; i < hex.length; i++)
624
+ if (internal_bytes.charCodeToBase16(hex.charCodeAt(i)) === undefined)
625
+ throw invalidNibble(hex as Hex.Hex)
626
+ }
627
+
461
628
  /**
462
629
  * Hex-leaf fast path: writes the RLP encoding of a hex string directly into
463
630
  * `bytes` by nibble-decoding the source hex chars into the destination
@@ -1,7 +1,8 @@
1
1
  import { secp256k1 } from '@noble/curves/secp256k1.js'
2
2
  import * as Address from './Address.js'
3
3
  import * as Bytes from './Bytes.js'
4
- import type * as Errors from './Errors.js'
4
+ import * as Errors from './Errors.js'
5
+ import * as Hash from './Hash.js'
5
6
  import * as Hex from './Hex.js'
6
7
  import {
7
8
  formatPublicKey,
@@ -71,6 +72,75 @@ export declare namespace createKeyPair {
71
72
  | Errors.GlobalErrorType
72
73
  }
73
74
 
75
+ /**
76
+ * Derives a valid secp256k1 private key from a 32-byte WebAuthn PRF output.
77
+ *
78
+ * The permanent derivation contract uses the PRF output as the HMAC-SHA256
79
+ * key. Its message is the UTF-8 bytes of `ox.secp256k1.fromPrf.v1` followed by
80
+ * a 32-bit big-endian counter starting at zero. Invalid scalars are skipped.
81
+ *
82
+ * @example
83
+ * ```ts twoslash
84
+ * import { Secp256k1 } from 'ox'
85
+ *
86
+ * const privateKey = Secp256k1.fromPrf(
87
+ * '0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
88
+ * )
89
+ * ```
90
+ *
91
+ * @param value - A 32-byte WebAuthn PRF output.
92
+ * @param options - Options.
93
+ * @returns A valid secp256k1 private key.
94
+ */
95
+ export function fromPrf<as extends 'Hex' | 'Bytes' = 'Hex'>(
96
+ value: Hex.Hex | Bytes.Bytes,
97
+ options: fromPrf.Options<as> = {},
98
+ ): fromPrf.ReturnType<as> {
99
+ const { as = 'Hex' } = options
100
+ const bytes = Bytes.from(value)
101
+ if (bytes.length !== 32) throw new InvalidPrfSizeError({ size: bytes.length })
102
+
103
+ for (let counter = 0; ; counter++) {
104
+ const candidate = Hash.hmac256(
105
+ bytes,
106
+ Bytes.concat(fromPrfLabel, Bytes.fromNumber(counter, { size: 4 })),
107
+ { as: 'Bytes' },
108
+ )
109
+ if (noble.utils.isValidSecretKey(candidate)) {
110
+ if (as === 'Hex') {
111
+ const value = Hex.fromBytes(candidate)
112
+ candidate.fill(0)
113
+ return value as never
114
+ }
115
+ return candidate as never
116
+ }
117
+ candidate.fill(0)
118
+ }
119
+ }
120
+
121
+ export declare namespace fromPrf {
122
+ type Options<as extends 'Hex' | 'Bytes' = 'Hex'> = {
123
+ /**
124
+ * Format of the returned private key.
125
+ * @default 'Hex'
126
+ */
127
+ as?: as | 'Hex' | 'Bytes' | undefined
128
+ }
129
+
130
+ type ReturnType<as extends 'Hex' | 'Bytes'> =
131
+ | (as extends 'Bytes' ? Bytes.Bytes : never)
132
+ | (as extends 'Hex' ? Hex.Hex : never)
133
+
134
+ type ErrorType =
135
+ | Bytes.concat.ErrorType
136
+ | Bytes.from.ErrorType
137
+ | Bytes.fromNumber.ErrorType
138
+ | Hash.hmac256.ErrorType
139
+ | Hex.fromBytes.ErrorType
140
+ | InvalidPrfSizeError
141
+ | Errors.GlobalErrorType
142
+ }
143
+
74
144
  /**
75
145
  * Computes the secp256k1 ECDSA public key from a provided private key.
76
146
  *
@@ -503,3 +573,24 @@ export declare namespace verify {
503
573
 
504
574
  type ErrorType = Errors.GlobalErrorType
505
575
  }
576
+
577
+ /** Thrown when a WebAuthn PRF output is not 32 bytes. */
578
+ export class InvalidPrfSizeError extends Errors.BaseError {
579
+ override readonly name = 'Secp256k1.InvalidPrfSizeError'
580
+
581
+ constructor(options: InvalidPrfSizeError.Options) {
582
+ super(
583
+ `PRF output must be exactly 32 bytes. Received ${options.size} bytes.`,
584
+ )
585
+ }
586
+ }
587
+
588
+ export declare namespace InvalidPrfSizeError {
589
+ /** Options for {@link ox#Secp256k1.InvalidPrfSizeError}. */
590
+ type Options = {
591
+ /** Received PRF output size. */
592
+ size: number
593
+ }
594
+ }
595
+
596
+ const fromPrfLabel = Bytes.fromString('ox.secp256k1.fromPrf.v1')