ox 1.0.4 → 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 +62 -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/ZoneId.d.ts +35 -17
- package/dist/tempo/ZoneId.d.ts.map +1 -1
- package/dist/tempo/ZoneId.js +41 -16
- package/dist/tempo/ZoneId.js.map +1 -1
- package/dist/tempo/index.d.ts +5 -6
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +5 -6
- package/dist/tempo/index.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/ZoneId.test-d.ts +16 -0
- package/src/tempo/ZoneId.test.ts +48 -13
- package/src/tempo/ZoneId.ts +55 -19
- package/src/tempo/index.ts +5 -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/zod/tempo/_test/ZoneId.test.ts +2 -2
- package/src/tempo/internal/mine.c +0 -182
|
@@ -3,8 +3,7 @@ import { AbiParameters } from 'ox'
|
|
|
3
3
|
import { describe, expect } from 'vp/test'
|
|
4
4
|
|
|
5
5
|
import { arbitraryAbiCase } from '../../../test/fuzz/arbitraries/abi.js'
|
|
6
|
-
|
|
7
|
-
const numRuns = Number(process.env.FC_NUM_RUNS) || 100
|
|
6
|
+
import { numRuns } from '../../../test/fuzz/numRuns.js'
|
|
8
7
|
|
|
9
8
|
describe('AbiParameters round-trip', () => {
|
|
10
9
|
test.prop(
|
|
@@ -3,8 +3,7 @@ import { Base32, Base58, Base64 } from 'ox'
|
|
|
3
3
|
import { describe, expect } from 'vp/test'
|
|
4
4
|
|
|
5
5
|
import { arbitraryBytes } from '../../../test/fuzz/arbitraries/bytes.js'
|
|
6
|
-
|
|
7
|
-
const numRuns = Number(process.env.FC_NUM_RUNS) || 100
|
|
6
|
+
import { numRuns } from '../../../test/fuzz/numRuns.js'
|
|
8
7
|
|
|
9
8
|
describe('Base32 round-trip', () => {
|
|
10
9
|
test.prop({ bytes: arbitraryBytes() }, { numRuns })(
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { fc, test } from '@fast-check/vitest'
|
|
2
2
|
import { Cbor } from 'ox'
|
|
3
3
|
import { describe, expect } from 'vp/test'
|
|
4
|
-
|
|
5
|
-
const numRuns = Number(process.env.FC_NUM_RUNS) || 100
|
|
4
|
+
import { numRuns } from '../../../test/fuzz/numRuns.js'
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Recursive CBOR-encodable value. ox's Cbor module supports:
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { fc, test } from '@fast-check/vitest'
|
|
2
|
+
import {
|
|
3
|
+
Bls,
|
|
4
|
+
Ed25519,
|
|
5
|
+
Engine,
|
|
6
|
+
Hash,
|
|
7
|
+
Keystore,
|
|
8
|
+
Mnemonic,
|
|
9
|
+
P256,
|
|
10
|
+
Secp256k1,
|
|
11
|
+
X25519,
|
|
12
|
+
} from 'ox'
|
|
13
|
+
import { afterEach, describe, expect } from 'vp/test'
|
|
14
|
+
import { identity, sentinel } from '../../../test/engines.js'
|
|
15
|
+
import { numRuns } from '../../../test/fuzz/numRuns.js'
|
|
16
|
+
|
|
17
|
+
const arbitraryBytes = fc.uint8Array({ maxLength: 256, minLength: 0 })
|
|
18
|
+
/**
|
|
19
|
+
* A 32-byte scalar that is a valid private key on every curve ox uses.
|
|
20
|
+
*
|
|
21
|
+
* Built compositionally rather than by filtering, so no candidate is ever
|
|
22
|
+
* rejected: a leading byte in `[0x01, 0x72]` is clear of zero and below every
|
|
23
|
+
* order in play. BLS12-381 sets the ceiling, not the 256-bit curves -- its
|
|
24
|
+
* scalar field is `0x73eda753…`, so anything above `0x72` reduces rather than
|
|
25
|
+
* being used as given, which would quietly narrow what the fuzz explores.
|
|
26
|
+
*/
|
|
27
|
+
const arbitraryPrivateKey = fc
|
|
28
|
+
.tuple(
|
|
29
|
+
fc.integer({ max: 0x72, min: 0x01 }),
|
|
30
|
+
fc.uint8Array({ maxLength: 31, minLength: 31 }),
|
|
31
|
+
)
|
|
32
|
+
.map(([first, rest]) => Uint8Array.of(first, ...rest))
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
Engine.reset()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Each entry runs one routed call site. `run` must be deterministic given its
|
|
40
|
+
* inputs, so that the same call before and after installing an engine can be
|
|
41
|
+
* compared directly.
|
|
42
|
+
*/
|
|
43
|
+
const callSites = [
|
|
44
|
+
{
|
|
45
|
+
name: 'Hash.keccak256',
|
|
46
|
+
run: (bytes: Uint8Array) => Hash.keccak256(bytes),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'Hash.sha256',
|
|
50
|
+
run: (bytes: Uint8Array) => Hash.sha256(bytes),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'Hash.ripemd160',
|
|
54
|
+
run: (bytes: Uint8Array) => Hash.ripemd160(bytes),
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'Hash.hmac256',
|
|
58
|
+
run: (bytes: Uint8Array) => Hash.hmac256(new Uint8Array(32).fill(7), bytes),
|
|
59
|
+
},
|
|
60
|
+
] as const
|
|
61
|
+
|
|
62
|
+
describe('identity engine', () => {
|
|
63
|
+
test.prop({ bytes: arbitraryBytes }, { numRuns })(
|
|
64
|
+
'leaves every hash call site unchanged',
|
|
65
|
+
({ bytes }) => {
|
|
66
|
+
const before = callSites.map((site) => site.run(bytes))
|
|
67
|
+
Engine.set(identity)
|
|
68
|
+
expect(callSites.map((site) => site.run(bytes))).toEqual(before)
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
test.prop(
|
|
73
|
+
{ payload: arbitraryBytes, privateKey: arbitraryPrivateKey },
|
|
74
|
+
{
|
|
75
|
+
numRuns,
|
|
76
|
+
},
|
|
77
|
+
)('leaves secp256k1 unchanged', ({ payload, privateKey }) => {
|
|
78
|
+
const before = {
|
|
79
|
+
publicKey: Secp256k1.getPublicKey({ privateKey }),
|
|
80
|
+
signature: Secp256k1.sign({ payload, privateKey }),
|
|
81
|
+
}
|
|
82
|
+
const recovered = Secp256k1.recoverPublicKey({
|
|
83
|
+
payload,
|
|
84
|
+
signature: before.signature,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
Engine.set(identity)
|
|
88
|
+
|
|
89
|
+
expect(Secp256k1.getPublicKey({ privateKey })).toEqual(before.publicKey)
|
|
90
|
+
expect(Secp256k1.sign({ payload, privateKey })).toEqual(before.signature)
|
|
91
|
+
expect(
|
|
92
|
+
Secp256k1.recoverPublicKey({ payload, signature: before.signature }),
|
|
93
|
+
).toEqual(recovered)
|
|
94
|
+
expect(
|
|
95
|
+
Secp256k1.verify({
|
|
96
|
+
payload,
|
|
97
|
+
publicKey: before.publicKey,
|
|
98
|
+
signature: before.signature,
|
|
99
|
+
}),
|
|
100
|
+
).toBe(true)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test.prop(
|
|
104
|
+
{ payload: arbitraryBytes, privateKey: arbitraryPrivateKey },
|
|
105
|
+
{
|
|
106
|
+
numRuns,
|
|
107
|
+
},
|
|
108
|
+
)('leaves P256 unchanged', ({ payload, privateKey }) => {
|
|
109
|
+
const publicKey = P256.getPublicKey({ privateKey })
|
|
110
|
+
const signature = P256.sign({ payload, privateKey })
|
|
111
|
+
|
|
112
|
+
Engine.set(identity)
|
|
113
|
+
|
|
114
|
+
expect(P256.getPublicKey({ privateKey })).toEqual(publicKey)
|
|
115
|
+
expect(P256.sign({ payload, privateKey })).toEqual(signature)
|
|
116
|
+
expect(P256.verify({ payload, publicKey, signature })).toBe(true)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test.prop(
|
|
120
|
+
{ payload: arbitraryBytes, privateKey: arbitraryPrivateKey },
|
|
121
|
+
{ numRuns },
|
|
122
|
+
)('leaves Ed25519 and X25519 unchanged', ({ payload, privateKey }) => {
|
|
123
|
+
const before = {
|
|
124
|
+
ed25519PublicKey: Ed25519.getPublicKey({ privateKey }),
|
|
125
|
+
signature: Ed25519.sign({ payload, privateKey }),
|
|
126
|
+
x25519PublicKey: X25519.getPublicKey({ privateKey }),
|
|
127
|
+
}
|
|
128
|
+
const sharedSecret = X25519.getSharedSecret({
|
|
129
|
+
privateKey,
|
|
130
|
+
publicKey: before.x25519PublicKey,
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
Engine.set(identity)
|
|
134
|
+
|
|
135
|
+
expect(Ed25519.getPublicKey({ privateKey })).toEqual(
|
|
136
|
+
before.ed25519PublicKey,
|
|
137
|
+
)
|
|
138
|
+
expect(Ed25519.sign({ payload, privateKey })).toEqual(before.signature)
|
|
139
|
+
expect(X25519.getPublicKey({ privateKey })).toEqual(before.x25519PublicKey)
|
|
140
|
+
expect(
|
|
141
|
+
X25519.getSharedSecret({
|
|
142
|
+
privateKey,
|
|
143
|
+
publicKey: before.x25519PublicKey,
|
|
144
|
+
}),
|
|
145
|
+
).toEqual(sharedSecret)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test.prop({ password: fc.string({ maxLength: 64 }) }, { numRuns: 10 })(
|
|
149
|
+
'leaves Keystore key derivation unchanged',
|
|
150
|
+
({ password }) => {
|
|
151
|
+
// The smallest parameters ox accepts -- these run per { numRuns } case, and
|
|
152
|
+
// scrypt's real defaults take seconds. `iv` is pinned because it defaults
|
|
153
|
+
// to random, and the returned key is a thunk, so compare derived material
|
|
154
|
+
// rather than the returned tuple.
|
|
155
|
+
const options = {
|
|
156
|
+
iv: '0x6087dab2f9fdbbfaddc31a909735c1e6',
|
|
157
|
+
password,
|
|
158
|
+
salt: '0xae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd',
|
|
159
|
+
} as const
|
|
160
|
+
const before = {
|
|
161
|
+
pbkdf2: Keystore.pbkdf2({ ...options, iterations: 1000 })[0](),
|
|
162
|
+
scrypt: Keystore.scrypt({ ...options, n: 1024, p: 1, r: 1 })[0](),
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
Engine.set(identity)
|
|
166
|
+
|
|
167
|
+
expect(Keystore.pbkdf2({ ...options, iterations: 1000 })[0]()).toEqual(
|
|
168
|
+
before.pbkdf2,
|
|
169
|
+
)
|
|
170
|
+
expect(Keystore.scrypt({ ...options, n: 1024, p: 1, r: 1 })[0]()).toEqual(
|
|
171
|
+
before.scrypt,
|
|
172
|
+
)
|
|
173
|
+
},
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
test.prop(
|
|
177
|
+
{ payload: arbitraryBytes, privateKey: arbitraryPrivateKey },
|
|
178
|
+
{ numRuns: Math.min(numRuns, 10) },
|
|
179
|
+
)('leaves Bls unchanged', ({ payload, privateKey }) => {
|
|
180
|
+
const before = {
|
|
181
|
+
publicKey: Bls.getPublicKey({ privateKey }),
|
|
182
|
+
signature: Bls.sign({ payload, privateKey }),
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
Engine.set(identity)
|
|
186
|
+
|
|
187
|
+
expect(Bls.getPublicKey({ privateKey })).toEqual(before.publicKey)
|
|
188
|
+
expect(Bls.sign({ payload, privateKey })).toEqual(before.signature)
|
|
189
|
+
expect(
|
|
190
|
+
Bls.verify({
|
|
191
|
+
payload,
|
|
192
|
+
publicKey: before.publicKey,
|
|
193
|
+
signature: before.signature,
|
|
194
|
+
}),
|
|
195
|
+
).toBe(true)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test.prop({ passphrase: fc.string({ maxLength: 32 }) }, { numRuns })(
|
|
199
|
+
'leaves Mnemonic.toSeed unchanged',
|
|
200
|
+
({ passphrase }) => {
|
|
201
|
+
const mnemonic =
|
|
202
|
+
'test test test test test test test test test test test junk'
|
|
203
|
+
const before = Mnemonic.toSeed(mnemonic, { passphrase })
|
|
204
|
+
Engine.set(identity)
|
|
205
|
+
expect(Mnemonic.toSeed(mnemonic, { passphrase })).toEqual(before)
|
|
206
|
+
},
|
|
207
|
+
)
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
describe('sentinel engine', () => {
|
|
211
|
+
test.prop({ bytes: arbitraryBytes }, { numRuns })(
|
|
212
|
+
'is consulted by every hash call site',
|
|
213
|
+
({ bytes }) => {
|
|
214
|
+
// The inverse of the identity property: if a result survives a wrong
|
|
215
|
+
// implementation, the call site never reached the engine at all.
|
|
216
|
+
const before = callSites.map((site) => site.run(bytes))
|
|
217
|
+
Engine.set(sentinel)
|
|
218
|
+
const after = callSites.map((site) => site.run(bytes))
|
|
219
|
+
for (const [index, site] of callSites.entries())
|
|
220
|
+
expect(after[index], site.name).not.toEqual(before[index])
|
|
221
|
+
},
|
|
222
|
+
)
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
describe('with', () => {
|
|
226
|
+
test.prop(
|
|
227
|
+
{ bytes: arbitraryBytes, depth: fc.integer({ max: 6, min: 1 }) },
|
|
228
|
+
{ numRuns },
|
|
229
|
+
)('restores the engine at every nesting depth', ({ bytes, depth }) => {
|
|
230
|
+
const expected = Hash.keccak256(bytes)
|
|
231
|
+
|
|
232
|
+
function nest(level: number): void {
|
|
233
|
+
if (level === 0) return
|
|
234
|
+
Engine.with(
|
|
235
|
+
{ Hash: { keccak256: () => new Uint8Array(32).fill(level) } },
|
|
236
|
+
() => {
|
|
237
|
+
expect(Hash.keccak256(bytes, { as: 'Bytes' })[0]).toBe(level)
|
|
238
|
+
nest(level - 1)
|
|
239
|
+
// Unwinding must restore *this* level, not the default.
|
|
240
|
+
expect(Hash.keccak256(bytes, { as: 'Bytes' })[0]).toBe(level)
|
|
241
|
+
},
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
nest(depth)
|
|
246
|
+
expect(Engine.get()).toEqual({})
|
|
247
|
+
expect(Hash.keccak256(bytes)).toEqual(expected)
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
test.prop({ bytes: arbitraryBytes }, { numRuns })(
|
|
251
|
+
'restores the engine when the callback throws',
|
|
252
|
+
({ bytes }) => {
|
|
253
|
+
const expected = Hash.keccak256(bytes)
|
|
254
|
+
expect(() =>
|
|
255
|
+
Engine.with({ Hash: { keccak256: () => new Uint8Array(32) } }, () => {
|
|
256
|
+
throw new Error('boom')
|
|
257
|
+
}),
|
|
258
|
+
).toThrow('boom')
|
|
259
|
+
expect(Hash.keccak256(bytes)).toEqual(expected)
|
|
260
|
+
},
|
|
261
|
+
)
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
describe('merge', () => {
|
|
265
|
+
const slotNames = ['Hash', 'Secp256k1', 'P256', 'Ed25519', 'X25519'] as const
|
|
266
|
+
|
|
267
|
+
test.prop(
|
|
268
|
+
{
|
|
269
|
+
slots: fc.array(fc.constantFrom(...slotNames), {
|
|
270
|
+
maxLength: 8,
|
|
271
|
+
minLength: 1,
|
|
272
|
+
}),
|
|
273
|
+
},
|
|
274
|
+
{ numRuns },
|
|
275
|
+
)('accumulates slots across calls in any order', ({ slots }) => {
|
|
276
|
+
for (const slot of slots) Engine.set({ [slot]: {} })
|
|
277
|
+
expect(Object.keys(Engine.get()).sort()).toEqual([...new Set(slots)].sort())
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
test.prop(
|
|
281
|
+
{
|
|
282
|
+
slots: fc.array(fc.constantFrom(...slotNames), {
|
|
283
|
+
maxLength: 8,
|
|
284
|
+
minLength: 1,
|
|
285
|
+
}),
|
|
286
|
+
},
|
|
287
|
+
{ numRuns },
|
|
288
|
+
)('reset clears whatever was accumulated', ({ slots }) => {
|
|
289
|
+
for (const slot of slots) Engine.set({ [slot]: {} })
|
|
290
|
+
Engine.reset()
|
|
291
|
+
expect(Engine.get()).toEqual({})
|
|
292
|
+
})
|
|
293
|
+
})
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Engine } from 'ox'
|
|
2
|
+
import { describe, expectTypeOf, test } from 'vp/test'
|
|
3
|
+
import type { primitives } from '../internal/engine.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Primitives on a slot contract that the runtime name table omits.
|
|
7
|
+
*
|
|
8
|
+
* `set` rejects any name absent from that table, so anything showing up here
|
|
9
|
+
* would be a valid override refused at runtime.
|
|
10
|
+
*/
|
|
11
|
+
type Unlisted = {
|
|
12
|
+
// `-?` because the slots are optional, which would otherwise leave
|
|
13
|
+
// `undefined` in the union and mask a genuinely missing name.
|
|
14
|
+
[key in keyof Engine.Engine]-?: Exclude<
|
|
15
|
+
keyof NonNullable<Engine.Engine[key]>,
|
|
16
|
+
(typeof primitives)[key][number]
|
|
17
|
+
>
|
|
18
|
+
}[keyof Engine.Engine]
|
|
19
|
+
|
|
20
|
+
describe('set', () => {
|
|
21
|
+
test('accepts a partial engine', () => {
|
|
22
|
+
expectTypeOf(Engine.set).parameter(0).toEqualTypeOf<Engine.Engine>()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('rejects an unknown slot', () => {
|
|
26
|
+
// @ts-expect-error
|
|
27
|
+
Engine.set({ Keccak: {} })
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('rejects a misspelled function', () => {
|
|
31
|
+
// @ts-expect-error
|
|
32
|
+
Engine.set({ Hash: { keccak_256: (input: Uint8Array) => input } })
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('rejects a mistyped function', () => {
|
|
36
|
+
// @ts-expect-error
|
|
37
|
+
Engine.set({ Hash: { keccak256: (input: string) => input } })
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('the runtime name table lists every primitive it must accept', () => {
|
|
41
|
+
expectTypeOf<Unlisted>().toEqualTypeOf<never>()
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('get', () => {
|
|
46
|
+
test('default', () => {
|
|
47
|
+
expectTypeOf(Engine.get()).toEqualTypeOf<Engine.Engine>()
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe('reset', () => {
|
|
52
|
+
test('accepts a slot name', () => {
|
|
53
|
+
expectTypeOf(Engine.reset)
|
|
54
|
+
.parameter(0)
|
|
55
|
+
.toEqualTypeOf<keyof Engine.Engine | undefined>()
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
describe('with', () => {
|
|
60
|
+
test('preserves the return type', () => {
|
|
61
|
+
expectTypeOf(Engine.with({}, () => 1 as const)).toEqualTypeOf<1>()
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
describe('Engine', () => {
|
|
66
|
+
test('the Secp256k1 and P256 slots share the Ecdsa contract', () => {
|
|
67
|
+
expectTypeOf<Engine.Engine['Secp256k1']>().toEqualTypeOf<
|
|
68
|
+
Engine.Ecdsa | undefined
|
|
69
|
+
>()
|
|
70
|
+
expectTypeOf<Engine.Engine['P256']>().toEqualTypeOf<
|
|
71
|
+
Engine.Ecdsa | undefined
|
|
72
|
+
>()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('every slot is optional', () => {
|
|
76
|
+
expectTypeOf<Engine.Engine>().toEqualTypeOf<Partial<Engine.Engine>>()
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('contracts speak raw bytes, not branded ox types', () => {
|
|
80
|
+
expectTypeOf<NonNullable<Engine.Hash['keccak256']>>().toEqualTypeOf<
|
|
81
|
+
(input: Uint8Array) => Uint8Array
|
|
82
|
+
>()
|
|
83
|
+
})
|
|
84
|
+
})
|