ox 1.0.5 → 1.1.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.
- package/CHANGELOG.md +50 -0
- package/dist/core/Base64.d.ts +10 -21
- package/dist/core/Base64.d.ts.map +1 -1
- package/dist/core/Base64.js +8 -127
- package/dist/core/Base64.js.map +1 -1
- package/dist/core/BinaryStateTree.d.ts.map +1 -1
- package/dist/core/BinaryStateTree.js +2 -2
- package/dist/core/BinaryStateTree.js.map +1 -1
- package/dist/core/Bls.d.ts.map +1 -1
- package/dist/core/Bls.js +19 -44
- package/dist/core/Bls.js.map +1 -1
- package/dist/core/Ed25519.d.ts.map +1 -1
- package/dist/core/Ed25519.js +7 -6
- package/dist/core/Ed25519.js.map +1 -1
- package/dist/core/Engine.d.ts +124 -0
- package/dist/core/Engine.d.ts.map +1 -0
- package/dist/core/Engine.js +180 -0
- package/dist/core/Engine.js.map +1 -0
- package/dist/core/Hash.d.ts +4 -4
- package/dist/core/Hash.d.ts.map +1 -1
- package/dist/core/Hash.js +9 -12
- package/dist/core/Hash.js.map +1 -1
- package/dist/core/Keystore.d.ts.map +1 -1
- package/dist/core/Keystore.js +15 -10
- package/dist/core/Keystore.js.map +1 -1
- package/dist/core/Mnemonic.d.ts.map +1 -1
- package/dist/core/Mnemonic.js +3 -2
- package/dist/core/Mnemonic.js.map +1 -1
- package/dist/core/P256.d.ts.map +1 -1
- package/dist/core/P256.js +8 -9
- package/dist/core/P256.js.map +1 -1
- package/dist/core/Secp256k1.d.ts.map +1 -1
- package/dist/core/Secp256k1.js +8 -9
- package/dist/core/Secp256k1.js.map +1 -1
- package/dist/core/WebCryptoP256.d.ts.map +1 -1
- package/dist/core/WebCryptoP256.js +2 -2
- package/dist/core/WebCryptoP256.js.map +1 -1
- package/dist/core/X25519.d.ts.map +1 -1
- package/dist/core/X25519.js +4 -3
- package/dist/core/X25519.js.map +1 -1
- package/dist/core/internal/bls.d.ts +12 -0
- package/dist/core/internal/bls.d.ts.map +1 -0
- package/dist/core/internal/bls.js +79 -0
- package/dist/core/internal/bls.js.map +1 -0
- package/dist/core/internal/codec/base64.d.ts +84 -0
- package/dist/core/internal/codec/base64.d.ts.map +1 -0
- package/dist/core/internal/codec/base64.js +177 -0
- package/dist/core/internal/codec/base64.js.map +1 -0
- package/dist/core/internal/codec/hex.d.ts +49 -4
- package/dist/core/internal/codec/hex.d.ts.map +1 -1
- package/dist/core/internal/codec/hex.js +109 -35
- package/dist/core/internal/codec/hex.js.map +1 -1
- package/dist/core/internal/curves.d.ts +17 -0
- package/dist/core/internal/curves.d.ts.map +1 -0
- package/dist/core/internal/curves.js +17 -0
- package/dist/core/internal/curves.js.map +1 -0
- package/dist/core/internal/ed25519.d.ts +14 -0
- package/dist/core/internal/ed25519.d.ts.map +1 -0
- package/dist/core/internal/ed25519.js +29 -0
- package/dist/core/internal/ed25519.js.map +1 -0
- package/dist/core/internal/engine.d.ts +278 -0
- package/dist/core/internal/engine.d.ts.map +1 -0
- package/dist/core/internal/engine.js +131 -0
- package/dist/core/internal/engine.js.map +1 -0
- package/dist/core/internal/hash.d.ts +12 -0
- package/dist/core/internal/hash.d.ts.map +1 -0
- package/dist/core/internal/hash.js +30 -0
- package/dist/core/internal/hash.js.map +1 -0
- package/dist/core/internal/keystore.d.ts +14 -0
- package/dist/core/internal/keystore.d.ts.map +1 -0
- package/dist/core/internal/keystore.js +32 -0
- package/dist/core/internal/keystore.js.map +1 -0
- package/dist/core/internal/mnemonic.d.ts +4 -0
- package/dist/core/internal/mnemonic.d.ts.map +1 -0
- package/dist/core/internal/mnemonic.js +14 -0
- package/dist/core/internal/mnemonic.js.map +1 -0
- package/dist/core/internal/p256.d.ts +14 -0
- package/dist/core/internal/p256.d.ts.map +1 -0
- package/dist/core/internal/p256.js +36 -0
- package/dist/core/internal/p256.js.map +1 -0
- package/dist/core/internal/secp256k1.d.ts +14 -0
- package/dist/core/internal/secp256k1.d.ts.map +1 -0
- package/dist/core/internal/secp256k1.js +36 -0
- package/dist/core/internal/secp256k1.js.map +1 -0
- package/dist/core/internal/x25519.d.ts +8 -0
- package/dist/core/internal/x25519.d.ts.map +1 -0
- package/dist/core/internal/x25519.js +20 -0
- package/dist/core/internal/x25519.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.docs.d.ts +2 -0
- package/dist/index.docs.d.ts.map +1 -1
- package/dist/index.docs.js +2 -0
- package/dist/index.docs.js.map +1 -1
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -1
- package/dist/node/Engine.d.ts +55 -0
- package/dist/node/Engine.d.ts.map +1 -0
- package/dist/node/Engine.js +51 -0
- package/dist/node/Engine.js.map +1 -0
- package/dist/node/Hash.d.ts +39 -0
- package/dist/node/Hash.d.ts.map +1 -0
- package/dist/node/Hash.js +37 -0
- package/dist/node/Hash.js.map +1 -0
- package/dist/node/index.d.ts +39 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +39 -0
- package/dist/node/index.js.map +1 -0
- package/dist/tempo/VirtualMaster.d.ts +2 -0
- package/dist/tempo/VirtualMaster.d.ts.map +1 -1
- package/dist/tempo/VirtualMaster.js +19 -5
- package/dist/tempo/VirtualMaster.js.map +1 -1
- package/dist/tempo/internal/mine.wasm.d.ts +2 -4
- package/dist/tempo/internal/mine.wasm.d.ts.map +1 -1
- package/dist/tempo/internal/mine.wasm.js +7 -12
- package/dist/tempo/internal/mine.wasm.js.map +1 -1
- package/dist/tempo/internal/virtualMasterPool.js +4 -1
- package/dist/tempo/internal/virtualMasterPool.js.map +1 -1
- package/dist/wasm/Engine.d.ts +62 -0
- package/dist/wasm/Engine.d.ts.map +1 -0
- package/dist/wasm/Engine.js +57 -0
- package/dist/wasm/Engine.js.map +1 -0
- package/dist/wasm/Hash.d.ts +49 -0
- package/dist/wasm/Hash.d.ts.map +1 -0
- package/dist/wasm/Hash.js +70 -0
- package/dist/wasm/Hash.js.map +1 -0
- package/dist/wasm/index.d.ts +46 -0
- package/dist/wasm/index.d.ts.map +1 -0
- package/dist/wasm/index.js +46 -0
- package/dist/wasm/index.js.map +1 -0
- package/dist/wasm/internal/hash.d.ts +9 -0
- package/dist/wasm/internal/hash.d.ts.map +1 -0
- package/dist/wasm/internal/hash.js +26 -0
- package/dist/wasm/internal/hash.js.map +1 -0
- package/dist/wasm/internal/hashes.wasm.d.ts +5 -0
- package/dist/wasm/internal/hashes.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/hashes.wasm.js +12 -0
- package/dist/wasm/internal/hashes.wasm.js.map +1 -0
- package/dist/wasm/internal/instantiate.d.ts +56 -0
- package/dist/wasm/internal/instantiate.d.ts.map +1 -0
- package/dist/wasm/internal/instantiate.js +92 -0
- package/dist/wasm/internal/instantiate.js.map +1 -0
- package/dist/zod/TypedData.d.ts +12 -12
- package/package.json +36 -1
- package/src/core/Base64.ts +19 -166
- package/src/core/BinaryStateTree.ts +2 -3
- package/src/core/Bls.ts +27 -77
- package/src/core/Ed25519.ts +7 -7
- package/src/core/Engine.ts +235 -0
- package/src/core/Hash.ts +9 -12
- package/src/core/Keystore.ts +17 -17
- package/src/core/Mnemonic.ts +3 -6
- package/src/core/P256.ts +15 -24
- package/src/core/Rlp_tx.bench.ts +2 -4
- package/src/core/Secp256k1.ts +9 -14
- package/src/core/TxEnvelopeEip1559.bench.ts +0 -5
- package/src/core/WebCryptoP256.ts +2 -2
- package/src/core/X25519.ts +4 -3
- package/src/core/_test/AbiParameters.adversarial.fuzz.ts +1 -2
- package/src/core/_test/AbiParameters.fuzz.ts +1 -2
- package/src/core/_test/Base.fuzz.ts +1 -2
- package/src/core/_test/Cbor.fuzz.ts +1 -2
- package/src/core/_test/Engine.fuzz.ts +293 -0
- package/src/core/_test/Engine.test-d.ts +84 -0
- package/src/core/_test/Engine.test.ts +523 -0
- package/src/core/_test/Hash.vectors.test.ts +56 -0
- package/src/core/_test/Hex.fuzz.ts +1 -2
- package/src/core/_test/Rlp.fuzz.ts +1 -2
- package/src/core/_test/Signature.fuzz.ts +1 -2
- package/src/core/_test/curves.test.ts +7 -0
- package/src/core/_test/index.test.ts +1 -0
- package/src/core/internal/bls.ts +121 -0
- package/src/core/internal/codec/_test/base64.conformance.ts +156 -0
- package/src/core/internal/codec/_test/hex.conformance.ts +204 -0
- package/src/core/internal/codec/_test/hex.fuzz.ts +234 -0
- package/src/core/internal/codec/_test/hex.test.ts +124 -0
- package/src/core/internal/codec/base64.ts +222 -0
- package/src/core/internal/codec/hex.ts +115 -34
- package/src/core/internal/curves.ts +18 -0
- package/src/core/internal/ed25519.ts +64 -0
- package/src/core/internal/engine.ts +410 -0
- package/src/core/internal/hash.ts +48 -0
- package/src/core/internal/keystore.ts +107 -0
- package/src/core/internal/mnemonic.ts +18 -0
- package/src/core/internal/p256.ts +91 -0
- package/src/core/internal/secp256k1.ts +91 -0
- package/src/core/internal/x25519.ts +40 -0
- package/src/index.docs.ts +2 -0
- package/src/index.ts +36 -0
- package/src/node/Engine.ts +69 -0
- package/src/node/Hash.ts +56 -0
- package/src/node/_test/Engine.test.ts +62 -0
- package/src/node/_test/Hash.test-d.ts +26 -0
- package/src/node/_test/Hash.test.ts +102 -0
- package/src/node/index.ts +40 -0
- package/src/tempo/VirtualMaster.test.ts +75 -0
- package/src/tempo/VirtualMaster.ts +28 -6
- package/src/tempo/internal/mine.wasm.ts +7 -13
- package/src/tempo/internal/virtualMasterPool.ts +4 -1
- package/src/tsconfig.json +2 -1
- package/src/version.ts +1 -1
- package/src/wasm/Engine.ts +76 -0
- package/src/wasm/Hash.bench.ts +60 -0
- package/src/wasm/Hash.ts +109 -0
- package/src/wasm/_test/Engine.test.ts +62 -0
- package/src/wasm/_test/Hash.browser.test.ts +50 -0
- package/src/wasm/_test/Hash.fuzz.ts +188 -0
- package/src/wasm/_test/Hash.test-d.ts +28 -0
- package/src/wasm/_test/Hash.test.ts +211 -0
- package/src/wasm/_test/Hash.vectors.test.ts +117 -0
- package/src/wasm/_test/Runtime.test.ts +21 -0
- package/src/wasm/_test/runtime.wasm.ts +11 -0
- package/src/wasm/index.ts +47 -0
- package/src/wasm/internal/hash.ts +50 -0
- package/src/wasm/internal/hashes.wasm.ts +14 -0
- package/src/wasm/internal/instantiate.ts +135 -0
- package/src/tempo/internal/mine.c +0 -182
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Hash as CoreHash, Hex } from 'ox'
|
|
2
|
+
import { Hash } from 'ox/node'
|
|
3
|
+
import { describe, expect, test } from 'vp/test'
|
|
4
|
+
|
|
5
|
+
describe('create', () => {
|
|
6
|
+
test('behavior: exposes the supported primitives', async () => {
|
|
7
|
+
const engine = await Hash.create()
|
|
8
|
+
|
|
9
|
+
expect(Object.keys(engine.Hash).sort()).toMatchInlineSnapshot(`
|
|
10
|
+
[
|
|
11
|
+
"hmacSha256",
|
|
12
|
+
"ripemd160",
|
|
13
|
+
"sha256",
|
|
14
|
+
]
|
|
15
|
+
`)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('behavior: matches published empty-input vectors', async () => {
|
|
19
|
+
const { hmacSha256, ripemd160, sha256 } = (await Hash.create()).Hash
|
|
20
|
+
const empty = new Uint8Array()
|
|
21
|
+
|
|
22
|
+
expect(Hex.fromBytes(sha256(empty))).toMatchInlineSnapshot(
|
|
23
|
+
`"0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"`,
|
|
24
|
+
)
|
|
25
|
+
expect(Hex.fromBytes(ripemd160(empty))).toMatchInlineSnapshot(
|
|
26
|
+
`"0x9c1185a5c5e9fc54612808977ee8f548b2258d31"`,
|
|
27
|
+
)
|
|
28
|
+
expect(Hex.fromBytes(hmacSha256(empty, empty))).toMatchInlineSnapshot(
|
|
29
|
+
`"0xb613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"`,
|
|
30
|
+
)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('behavior: agrees with the default across block boundaries', async () => {
|
|
34
|
+
const node = (await Hash.create()).Hash
|
|
35
|
+
|
|
36
|
+
for (const size of [0, 1, 55, 56, 63, 64, 65, 127, 128, 129]) {
|
|
37
|
+
const input = Uint8Array.from({ length: size }, (_, index) => index % 251)
|
|
38
|
+
expect(node.sha256(input)).toEqual(
|
|
39
|
+
CoreHash.sha256(input, { as: 'Bytes' }),
|
|
40
|
+
)
|
|
41
|
+
expect(node.ripemd160(input)).toEqual(
|
|
42
|
+
CoreHash.ripemd160(input, { as: 'Bytes' }),
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('behavior: agrees with the default across HMAC key sizes', async () => {
|
|
48
|
+
const { hmacSha256 } = (await Hash.create()).Hash
|
|
49
|
+
const message = Uint8Array.from({ length: 65 }, (_, index) => index % 251)
|
|
50
|
+
|
|
51
|
+
for (const size of [0, 1, 32, 63, 64, 65, 200]) {
|
|
52
|
+
const key = Uint8Array.from({ length: size }, (_, index) => index % 239)
|
|
53
|
+
expect(hmacSha256(key, message)).toEqual(
|
|
54
|
+
CoreHash.hmac256(key, message, { as: 'Bytes' }),
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('behavior: respects typed-array offsets', async () => {
|
|
60
|
+
const node = (await Hash.create()).Hash
|
|
61
|
+
const input = Uint8Array.from(
|
|
62
|
+
{ length: 80 },
|
|
63
|
+
(_, index) => (index * 17) % 251,
|
|
64
|
+
).subarray(7, 71)
|
|
65
|
+
const key = Uint8Array.from(
|
|
66
|
+
{ length: 48 },
|
|
67
|
+
(_, index) => (index * 29) % 239,
|
|
68
|
+
).subarray(5, 37)
|
|
69
|
+
|
|
70
|
+
expect(node.sha256(input)).toEqual(CoreHash.sha256(input, { as: 'Bytes' }))
|
|
71
|
+
expect(node.ripemd160(input)).toEqual(
|
|
72
|
+
CoreHash.ripemd160(input, { as: 'Bytes' }),
|
|
73
|
+
)
|
|
74
|
+
expect(node.hmacSha256(key, input)).toEqual(
|
|
75
|
+
CoreHash.hmac256(key, input, { as: 'Bytes' }),
|
|
76
|
+
)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('behavior: returns a fresh engine', async () => {
|
|
80
|
+
const first = await Hash.create()
|
|
81
|
+
const second = await Hash.create()
|
|
82
|
+
|
|
83
|
+
expect(first === second).toMatchInlineSnapshot('false')
|
|
84
|
+
expect(first.Hash === second.Hash).toMatchInlineSnapshot('false')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('behavior: returns owned Uint8Array values', async () => {
|
|
88
|
+
const { hmacSha256, ripemd160, sha256 } = (await Hash.create()).Hash
|
|
89
|
+
const input = Uint8Array.of(1, 2, 3)
|
|
90
|
+
const outputs = [hmacSha256(input, input), ripemd160(input), sha256(input)]
|
|
91
|
+
|
|
92
|
+
expect(outputs.map((output) => output.constructor === Uint8Array))
|
|
93
|
+
.toMatchInlineSnapshot(`
|
|
94
|
+
[
|
|
95
|
+
true,
|
|
96
|
+
true,
|
|
97
|
+
true,
|
|
98
|
+
]
|
|
99
|
+
`)
|
|
100
|
+
expect(sha256(input) === sha256(input)).toMatchInlineSnapshot('false')
|
|
101
|
+
})
|
|
102
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** @entrypointCategory Node */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Node.js implementations of Ox's hash primitives, backed by `node:crypto`.
|
|
5
|
+
*
|
|
6
|
+
* Import this entrypoint only in Node.js. Its static `node:crypto` dependency
|
|
7
|
+
* is intentionally kept outside Ox's runtime-neutral entrypoint.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts twoslash
|
|
11
|
+
* // @noErrors
|
|
12
|
+
* import { Hash } from 'ox'
|
|
13
|
+
* import { Engine } from 'ox/node'
|
|
14
|
+
*
|
|
15
|
+
* await Engine.load()
|
|
16
|
+
*
|
|
17
|
+
* Hash.sha256('0xdeadbeef')
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @category Crypto
|
|
21
|
+
*/
|
|
22
|
+
export * as Engine from './Engine.js'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Node.js implementations of Ox's supported hash primitives.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts twoslash
|
|
29
|
+
* // @noErrors
|
|
30
|
+
* import { Engine, Hash } from 'ox'
|
|
31
|
+
* import * as NodeHash from 'ox/node/Hash'
|
|
32
|
+
*
|
|
33
|
+
* Engine.set(await NodeHash.create())
|
|
34
|
+
*
|
|
35
|
+
* Hash.sha256('0xdeadbeef')
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @category Crypto
|
|
39
|
+
*/
|
|
40
|
+
export * as Hash from './Hash.js'
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { Bytes } from 'ox'
|
|
1
2
|
import { VirtualMaster } from 'ox/tempo'
|
|
2
3
|
import { describe, expect, test } from 'vp/test'
|
|
4
|
+
import { instantiate } from '../wasm/internal/instantiate.js'
|
|
5
|
+
import { wasmBase64 } from './internal/mine.wasm.js'
|
|
3
6
|
|
|
4
7
|
const address = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
|
|
5
8
|
const salt =
|
|
@@ -123,6 +126,32 @@ describe('mineSaltAsync', () => {
|
|
|
123
126
|
`)
|
|
124
127
|
})
|
|
125
128
|
|
|
129
|
+
// `workerCount` is `min(workers, ceil(count / chunkSize))`, so the cases
|
|
130
|
+
// above collapse to one worker and take the JavaScript fallback. Only a
|
|
131
|
+
// chunk size small enough to need two workers reaches the WASM, which is how
|
|
132
|
+
// a miner writing over its own Keccak constants went unnoticed.
|
|
133
|
+
test('agrees with the fallback once the WASM actually runs', async () => {
|
|
134
|
+
const viaFallback = await VirtualMaster.mineSaltAsync({
|
|
135
|
+
address,
|
|
136
|
+
count: 16,
|
|
137
|
+
start: 0xabf52ba0n,
|
|
138
|
+
workers: 1,
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
const viaWasm = await VirtualMaster.mineSaltAsync({
|
|
142
|
+
address,
|
|
143
|
+
chunkSize: 8,
|
|
144
|
+
count: 16,
|
|
145
|
+
start: 0xabf52ba0n,
|
|
146
|
+
workers: 2,
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
expect(viaWasm).toEqual(viaFallback)
|
|
150
|
+
expect(viaWasm?.salt).toMatchInlineSnapshot(
|
|
151
|
+
`"0x00000000000000000000000000000000000000000000000000000000abf52baf"`,
|
|
152
|
+
)
|
|
153
|
+
})
|
|
154
|
+
|
|
126
155
|
test('finds the same salt with workers', async () => {
|
|
127
156
|
const result = await VirtualMaster.mineSaltAsync({
|
|
128
157
|
address,
|
|
@@ -150,6 +179,52 @@ describe('mineSaltAsync', () => {
|
|
|
150
179
|
expect(result).toBeUndefined()
|
|
151
180
|
})
|
|
152
181
|
|
|
182
|
+
test.each([
|
|
183
|
+
0,
|
|
184
|
+
-1,
|
|
185
|
+
1.5,
|
|
186
|
+
Number.NaN,
|
|
187
|
+
Number.POSITIVE_INFINITY,
|
|
188
|
+
2 ** 32,
|
|
189
|
+
Number.MAX_SAFE_INTEGER + 1,
|
|
190
|
+
])('rejects chunk size %s outside the uint32 ABI', async (chunkSize) => {
|
|
191
|
+
await expect(
|
|
192
|
+
VirtualMaster.mineSaltAsync({
|
|
193
|
+
address,
|
|
194
|
+
chunkSize,
|
|
195
|
+
count: 1,
|
|
196
|
+
workers: 1,
|
|
197
|
+
}),
|
|
198
|
+
).rejects.toThrow(
|
|
199
|
+
`Chunk size "${chunkSize}" is invalid. Expected a positive safe integer no greater than 4294967295.`,
|
|
200
|
+
)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
test('accepts the maximum uint32 chunk size', async () => {
|
|
204
|
+
await expect(
|
|
205
|
+
VirtualMaster.mineSaltAsync({
|
|
206
|
+
address,
|
|
207
|
+
chunkSize: 2 ** 32 - 1,
|
|
208
|
+
count: 1,
|
|
209
|
+
start: 0n,
|
|
210
|
+
workers: 1,
|
|
211
|
+
}),
|
|
212
|
+
).resolves.toBeUndefined()
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
test('treats the raw WASM count as unsigned', async () => {
|
|
216
|
+
type Exports = { mine(count: number): number }
|
|
217
|
+
const module = await instantiate<Exports>(wasmBase64)
|
|
218
|
+
module.reserve(84)
|
|
219
|
+
module.view().set(Bytes.fromHex(address), module.heapBase)
|
|
220
|
+
module.view().set(Bytes.fromHex(salt), module.heapBase + 20)
|
|
221
|
+
|
|
222
|
+
// 2 ** 31 has its sign bit set in the i32 passed by JavaScript. The first
|
|
223
|
+
// salt is already known to match, so unsigned C returns immediately; the old
|
|
224
|
+
// signed ABI interpreted the count as negative and skipped the loop.
|
|
225
|
+
expect(module.exports.mine(2 ** 31)).toBe(1)
|
|
226
|
+
})
|
|
227
|
+
|
|
153
228
|
test('reports progress', async () => {
|
|
154
229
|
const progressCalls: VirtualMaster.mineSaltAsync.Progress[] = []
|
|
155
230
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { keccak_256 } from '@noble/hashes/sha3.js'
|
|
2
1
|
import * as Address from '../core/Address.js'
|
|
3
2
|
import * as Bytes from '../core/Bytes.js'
|
|
4
3
|
import * as Errors from '../core/Errors.js'
|
|
5
4
|
import type * as Hash from '../core/Hash.js'
|
|
5
|
+
import * as hash_ from '../core/internal/hash.js'
|
|
6
6
|
import * as Hex from '../core/Hex.js'
|
|
7
7
|
import * as VirtualMasterPool from './internal/virtualMasterPool.js'
|
|
8
8
|
import * as VirtualAddress from './VirtualAddress.js'
|
|
9
9
|
|
|
10
10
|
const tip20Prefix = '0x20c000000000000000000000'
|
|
11
11
|
const zeroAddress = '0x0000000000000000000000000000000000000000'
|
|
12
|
+
const maxUint32 = 2 ** 32 - 1
|
|
12
13
|
|
|
13
14
|
/** A valid salt input for TIP-1022 master registration. */
|
|
14
15
|
export type Salt = Hex.Hex | Bytes.Bytes | number | bigint
|
|
@@ -203,7 +204,7 @@ export function mineSalt(
|
|
|
203
204
|
saltView.set(toFixedBytes(value.start ?? 0n, 32))
|
|
204
205
|
|
|
205
206
|
for (let i = 0; i < count; i++) {
|
|
206
|
-
const hash =
|
|
207
|
+
const hash = hash_.keccak256(input)
|
|
207
208
|
|
|
208
209
|
if (hash[0] === 0 && hash[1] === 0 && hash[2] === 0 && hash[3] === 0) {
|
|
209
210
|
return {
|
|
@@ -289,13 +290,14 @@ export async function mineSaltAsync(
|
|
|
289
290
|
workers = getDefaultWorkerCount(),
|
|
290
291
|
} = parameters
|
|
291
292
|
|
|
292
|
-
|
|
293
|
-
const start = toFixedHex(start_, 32)
|
|
294
|
-
|
|
293
|
+
assertChunkSize(chunkSize)
|
|
295
294
|
assertCount(count)
|
|
296
295
|
if (workers !== undefined) assertWorkers(workers)
|
|
297
296
|
throwIfAborted(signal)
|
|
298
297
|
|
|
298
|
+
const address = resolveAddress(parameters.address)
|
|
299
|
+
const start = toFixedHex(start_, 32)
|
|
300
|
+
|
|
299
301
|
const workerCount = Math.max(
|
|
300
302
|
1,
|
|
301
303
|
Math.min(workers, Math.ceil(count / chunkSize)),
|
|
@@ -341,6 +343,8 @@ export declare namespace mineSaltAsync {
|
|
|
341
343
|
/**
|
|
342
344
|
* Number of salts each worker processes before sending a progress update.
|
|
343
345
|
*
|
|
346
|
+
* Must be a positive integer no greater than 2 ** 32 - 1.
|
|
347
|
+
*
|
|
344
348
|
* @default 100_000
|
|
345
349
|
*/
|
|
346
350
|
chunkSize?: number | undefined
|
|
@@ -576,6 +580,24 @@ function assertWorkers(workers: number) {
|
|
|
576
580
|
)
|
|
577
581
|
}
|
|
578
582
|
|
|
583
|
+
/**
|
|
584
|
+
* Asserts that `chunkSize` fits the miner's unsigned 32-bit WASM ABI.
|
|
585
|
+
*
|
|
586
|
+
* @internal
|
|
587
|
+
*/
|
|
588
|
+
function assertChunkSize(chunkSize: number) {
|
|
589
|
+
if (
|
|
590
|
+
Number.isSafeInteger(chunkSize) &&
|
|
591
|
+
chunkSize > 0 &&
|
|
592
|
+
chunkSize <= maxUint32
|
|
593
|
+
)
|
|
594
|
+
return
|
|
595
|
+
|
|
596
|
+
throw new Errors.BaseError(
|
|
597
|
+
`Chunk size "${chunkSize}" is invalid. Expected a positive safe integer no greater than ${maxUint32}.`,
|
|
598
|
+
)
|
|
599
|
+
}
|
|
600
|
+
|
|
579
601
|
/**
|
|
580
602
|
* Extracts or creates an error from an `AbortSignal`.
|
|
581
603
|
*
|
|
@@ -679,7 +701,7 @@ function buildRegistrationDigest(
|
|
|
679
701
|
const addressBytes = Bytes.fromHex(resolveAddress(address))
|
|
680
702
|
buffer.set(addressBytes)
|
|
681
703
|
buffer.set(toFixedBytes(salt, 32), addressBytes.length)
|
|
682
|
-
return
|
|
704
|
+
return hash_.keccak256(buffer)
|
|
683
705
|
}
|
|
684
706
|
|
|
685
707
|
/**
|
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Generated by `pnpm wasm:build --target=mine`. Do not edit.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// [20..51] salt (32 bytes, set/read by JS, incremented in-place)
|
|
6
|
-
// [52..83] hash out (32 bytes, written on match)
|
|
3
|
+
// Compiled from wasm/src/mine.c, wasm/src/ox_rt.c
|
|
4
|
+
// with wasi-sdk 25.0 + binaryen 123.
|
|
7
5
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// Generated by: pnpm tsx scripts/build-mine-wasm.ts
|
|
6
|
+
// `pnpm wasm:check` rebuilds this file and fails if the bytes differ, so any
|
|
7
|
+
// change to the C or to `wasm/targets.ts` must be followed by `pnpm wasm:build`.
|
|
11
8
|
|
|
12
|
-
/** Base64-encoded WASM binary
|
|
9
|
+
/** Base64-encoded WASM binary. */
|
|
13
10
|
export const wasmBase64 =
|
|
14
|
-
'
|
|
15
|
-
|
|
16
|
-
/** Byte offset in WASM linear memory where mining I/O begins. */
|
|
17
|
-
export const dataOffset = 1024
|
|
11
|
+
'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'
|
|
@@ -185,7 +185,6 @@ main().catch(function(error) {
|
|
|
185
185
|
*/
|
|
186
186
|
function getWorkerMineLoop(): string {
|
|
187
187
|
return `
|
|
188
|
-
var dataOffset = 1024
|
|
189
188
|
|
|
190
189
|
function hexToBytes(hex) {
|
|
191
190
|
var h = hex.startsWith('0x') ? hex.slice(2) : hex
|
|
@@ -218,6 +217,10 @@ function mineLoop(data, wasm, mem, postMessage) {
|
|
|
218
217
|
var workerIndex = data.workerIndex
|
|
219
218
|
var startBigInt = BigInt(data.start)
|
|
220
219
|
|
|
220
|
+
// Asked of the module rather than baked in: the buffer starts past the
|
|
221
|
+
// static data, wherever the linker put its end.
|
|
222
|
+
var dataOffset = wasm.heap_base()
|
|
223
|
+
|
|
221
224
|
mem.set(addressBytes, dataOffset)
|
|
222
225
|
|
|
223
226
|
for (
|
package/src/tsconfig.json
CHANGED
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** @internal */
|
|
2
|
-
export const version = '1.0
|
|
2
|
+
export const version = '1.1.0'
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as CoreEngine from '../core/Engine.js'
|
|
2
|
+
import type * as Errors from '../core/Errors.js'
|
|
3
|
+
import * as Hash from './Hash.js'
|
|
4
|
+
import type * as internal from './internal/instantiate.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Compiles every implementation this entrypoint provides and installs it.
|
|
8
|
+
*
|
|
9
|
+
* WASM must be compiled asynchronously, so this is where the `await` lives.
|
|
10
|
+
* Everything afterwards, including the hashing itself, is synchronous.
|
|
11
|
+
*
|
|
12
|
+
* Call it once, during startup, before any crypto call. ox resolves the engine
|
|
13
|
+
* at call time, so values computed beforehand used whatever was installed then.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts twoslash
|
|
17
|
+
* // @noErrors
|
|
18
|
+
* import { Hash } from 'ox'
|
|
19
|
+
* import { Engine } from 'ox/wasm'
|
|
20
|
+
*
|
|
21
|
+
* await Engine.load()
|
|
22
|
+
*
|
|
23
|
+
* Hash.keccak256('0xdeadbeef')
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @returns The engine that was installed.
|
|
27
|
+
*/
|
|
28
|
+
export async function load(): Promise<load.ReturnType> {
|
|
29
|
+
const engine = await create()
|
|
30
|
+
CoreEngine.set(engine)
|
|
31
|
+
return engine
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export declare namespace load {
|
|
35
|
+
type ReturnType = create.ReturnType
|
|
36
|
+
|
|
37
|
+
type ErrorType =
|
|
38
|
+
| create.ErrorType
|
|
39
|
+
| CoreEngine.set.ErrorType
|
|
40
|
+
| Errors.GlobalErrorType
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Compiles every implementation this entrypoint provides, without installing
|
|
45
|
+
* it.
|
|
46
|
+
*
|
|
47
|
+
* Reach for this where an engine has to exist as a value: measuring one
|
|
48
|
+
* implementation against another, or installing through
|
|
49
|
+
* [`Engine.with`](/api/Engine/with) for the duration of a call. Combining
|
|
50
|
+
* engines does not need it, because
|
|
51
|
+
* [`Engine.set`](/api/Engine/set) merges -- `await load()` followed by
|
|
52
|
+
* `Engine.set({ Secp256k1 })` leaves both in place.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts twoslash
|
|
56
|
+
* // @noErrors
|
|
57
|
+
* import { Engine, Hash } from 'ox'
|
|
58
|
+
* import { Engine as Wasm } from 'ox/wasm'
|
|
59
|
+
*
|
|
60
|
+
* const wasm = await Wasm.create()
|
|
61
|
+
*
|
|
62
|
+
* Engine.with(wasm, () => Hash.keccak256('0xdeadbeef'))
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @returns An engine, ready to install.
|
|
66
|
+
*/
|
|
67
|
+
export async function create(): Promise<create.ReturnType> {
|
|
68
|
+
return { ...(await Hash.create()) }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export declare namespace create {
|
|
72
|
+
/** Every slot this entrypoint supplies, each with its primitives present. */
|
|
73
|
+
type ReturnType = Hash.create.ReturnType
|
|
74
|
+
|
|
75
|
+
type ErrorType = internal.MemoryError | Errors.GlobalErrorType
|
|
76
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { hmac } from '@noble/hashes/hmac.js'
|
|
2
|
+
import { ripemd160 as noble_ripemd160 } from '@noble/hashes/legacy.js'
|
|
3
|
+
import { sha256 as noble_sha256 } from '@noble/hashes/sha2.js'
|
|
4
|
+
import { keccak_256 as noble_keccak256 } from '@noble/hashes/sha3.js'
|
|
5
|
+
import { bench, describe } from 'vp/test'
|
|
6
|
+
import { create as createWasm } from './Hash.js'
|
|
7
|
+
|
|
8
|
+
// Sizes expose fixed boundary overhead and sustained throughput. Crossovers
|
|
9
|
+
// vary by primitive, runtime, and processor.
|
|
10
|
+
//
|
|
11
|
+
// Run `pnpm bench:engines` for the full engine comparison. Vitest cannot
|
|
12
|
+
// run Rust, and this portable benchmark must remain browser-compatible.
|
|
13
|
+
const sizes = [32, 64, 256, 1024, 4096, 65_536, 1_048_576] as const
|
|
14
|
+
|
|
15
|
+
const wasm = (await createWasm()).Hash
|
|
16
|
+
const key = Uint8Array.from({ length: 32 }, (_, index) => index % 97)
|
|
17
|
+
|
|
18
|
+
for (const size of sizes) {
|
|
19
|
+
const bytes = Uint8Array.from({ length: size }, (_, index) => index % 251)
|
|
20
|
+
|
|
21
|
+
describe(`keccak256 (${size} bytes input)`, () => {
|
|
22
|
+
bench('ox', () => {
|
|
23
|
+
noble_keccak256(bytes)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
bench('ox/wasm', () => {
|
|
27
|
+
wasm.keccak256(bytes)
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
describe(`sha256 (${size} bytes input)`, () => {
|
|
32
|
+
bench('ox', () => {
|
|
33
|
+
noble_sha256(bytes)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
bench('ox/wasm', () => {
|
|
37
|
+
wasm.sha256(bytes)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe(`ripemd160 (${size} bytes input)`, () => {
|
|
42
|
+
bench('ox', () => {
|
|
43
|
+
noble_ripemd160(bytes)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
bench('ox/wasm', () => {
|
|
47
|
+
wasm.ripemd160(bytes)
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe(`hmacSha256 (${size} bytes input)`, () => {
|
|
52
|
+
bench('ox', () => {
|
|
53
|
+
hmac(noble_sha256, key, bytes)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
bench('ox/wasm', () => {
|
|
57
|
+
wasm.hmacSha256(key, bytes)
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
}
|
package/src/wasm/Hash.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type * as Engine from '../core/Engine.js'
|
|
2
|
+
import type * as Errors from '../core/Errors.js'
|
|
3
|
+
import * as hash from './internal/hash.js'
|
|
4
|
+
import { wasmBase64 } from './internal/hashes.wasm.js'
|
|
5
|
+
import * as internal from './internal/instantiate.js'
|
|
6
|
+
|
|
7
|
+
export { MemoryError } from './internal/instantiate.js'
|
|
8
|
+
|
|
9
|
+
type Exports = hash.Exports & {
|
|
10
|
+
keccak256(input: number, length: number, out: number): void
|
|
11
|
+
ripemd160(input: number, length: number, out: number): void
|
|
12
|
+
sha256(input: number, length: number, out: number): void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Digest sizes, in bytes. */
|
|
16
|
+
const digestSize = { keccak256: 32, ripemd160: 20, sha256: 32 }
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Compilation is memoized; the engine object deliberately is not.
|
|
20
|
+
*
|
|
21
|
+
* Handing every caller the same object means one of them customising a slot
|
|
22
|
+
* for composition silently changes what a later `create` -- or `Engine.load`
|
|
23
|
+
* -- installs.
|
|
24
|
+
*
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
const instantiate = /*#__PURE__*/ internal.memoize(() =>
|
|
28
|
+
internal.instantiate<Exports>(wasmBase64),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Compiles the WASM implementation of the [`Hash`](/api/Hash) primitives, without
|
|
33
|
+
* installing it.
|
|
34
|
+
*
|
|
35
|
+
* Most callers want {@link ox#Engine.load} instead, which compiles every
|
|
36
|
+
* implementation this entrypoint provides and installs them in one call. Reach
|
|
37
|
+
* for this to take the `Hash` slot on its own, or to hold the implementation
|
|
38
|
+
* without touching the installed engine.
|
|
39
|
+
*
|
|
40
|
+
* :::note
|
|
41
|
+
* Performance varies by primitive, input size, runtime, and processor. Run
|
|
42
|
+
* `pnpm bench:engines` to compare Ox's default, Node, WASM, and Alloy
|
|
43
|
+
* implementations on your target machine.
|
|
44
|
+
* :::
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts twoslash
|
|
48
|
+
* // @noErrors
|
|
49
|
+
* import { Engine, Hash } from 'ox'
|
|
50
|
+
* import * as WasmHash from 'ox/wasm/Hash'
|
|
51
|
+
*
|
|
52
|
+
* Engine.set(await WasmHash.create())
|
|
53
|
+
*
|
|
54
|
+
* Hash.keccak256('0xdeadbeef')
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @returns An engine supplying the `Hash` slot.
|
|
58
|
+
*/
|
|
59
|
+
export async function create(): Promise<create.ReturnType> {
|
|
60
|
+
{
|
|
61
|
+
const module = await instantiate()
|
|
62
|
+
|
|
63
|
+
// Copies `input` in, runs `hash`, and copies the digest back out.
|
|
64
|
+
function call(
|
|
65
|
+
hash: 'keccak256' | 'ripemd160' | 'sha256',
|
|
66
|
+
input: Uint8Array,
|
|
67
|
+
): Uint8Array {
|
|
68
|
+
const out = digestSize[hash]
|
|
69
|
+
module.reserve(input.length + out)
|
|
70
|
+
const inputPtr = module.heapBase
|
|
71
|
+
const outPtr = inputPtr + input.length
|
|
72
|
+
module.view().set(input, inputPtr)
|
|
73
|
+
module.exports[hash](inputPtr, input.length, outPtr)
|
|
74
|
+
// `slice`, not `subarray`: the result must not alias WASM memory, which the
|
|
75
|
+
// next call overwrites and a `grow` detaches entirely.
|
|
76
|
+
return module.view().slice(outPtr, outPtr + out)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
Hash: {
|
|
81
|
+
hmacSha256: (key, message) => hash.hmacSha256(module, key, message),
|
|
82
|
+
keccak256: (input) => call('keccak256', input),
|
|
83
|
+
ripemd160: (input) => call('ripemd160', input),
|
|
84
|
+
sha256: (input) => call('sha256', input),
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export declare namespace create {
|
|
91
|
+
/**
|
|
92
|
+
* The `Hash` slot, carrying every primitive this module implements.
|
|
93
|
+
*
|
|
94
|
+
* {@link ox#Engine.Engine} is optional all the way down so that an engine can
|
|
95
|
+
* fill in as little as it likes. This one always fills the same four, and
|
|
96
|
+
* saying so is what spares callers a non-null assertion per primitive.
|
|
97
|
+
*/
|
|
98
|
+
type ReturnType = {
|
|
99
|
+
Hash: {
|
|
100
|
+
[key in
|
|
101
|
+
| 'hmacSha256'
|
|
102
|
+
| 'keccak256'
|
|
103
|
+
| 'ripemd160'
|
|
104
|
+
| 'sha256']-?: NonNullable<Engine.Hash[key]>
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
type ErrorType = internal.MemoryError | Errors.GlobalErrorType
|
|
109
|
+
}
|