@blindmarket/sdk 0.1.7 → 0.4.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/dist/chain/BlindEscrowClient.d.ts +2 -0
- package/dist/chain/BlindEscrowClient.js +14 -6
- package/dist/chain/BlindReputationClient.d.ts +3 -0
- package/dist/chain/BlindReputationClient.js +3 -1
- package/dist/chain/ChainClient.d.ts +5 -2
- package/dist/chain/ChainClient.js +5 -0
- package/dist/chain/IBlindMarketChain.d.ts +74 -0
- package/dist/chain/TaskRegistryClient.d.ts +2 -1
- package/dist/chain/TaskRegistryClient.js +15 -7
- package/dist/chain/createChainAdapter.d.ts +25 -0
- package/dist/chain/createChainAdapter.js +24 -0
- package/dist/chain/domain-types.d.ts +77 -0
- package/dist/chain/evm/EVMChainAdapter.d.ts +56 -0
- package/dist/chain/evm/EVMChainAdapter.js +161 -0
- package/dist/chain/evm/index.d.ts +1 -0
- package/dist/chain/evm/index.js +1 -0
- package/dist/chain/index.d.ts +4 -0
- package/dist/chain/index.js +5 -0
- package/dist/executor/WorkerRuntime.d.ts +134 -0
- package/dist/executor/WorkerRuntime.js +277 -0
- package/dist/executor/index.d.ts +1 -0
- package/dist/executor/index.js +1 -0
- package/dist/index.d.ts +82 -9
- package/dist/index.js +60 -6
- package/dist/network/presets.d.ts +33 -5
- package/dist/network/presets.js +35 -8
- package/dist/network/resolve.d.ts +12 -3
- package/dist/network/resolve.js +15 -7
- package/dist/network/types.d.ts +3 -16
- package/dist/types.d.ts +1 -1
- package/package.json +8 -1
- package/biome.json +0 -19
- package/dist/BlindEscrowClient-CyPuKMge.d.cts +0 -54
- package/dist/BlindEscrowClient-R7iGpE-n.d.ts +0 -54
- package/dist/KeyStore-Be_SSpCt.d.cts +0 -21
- package/dist/KeyStore-k1JgYPJY.d.ts +0 -21
- package/dist/Signer-BnqH_qew.d.cts +0 -38
- package/dist/Signer-LColE0Uo.d.ts +0 -38
- package/dist/Storage-4QOrh7rm.d.ts +0 -17
- package/dist/Storage-Cf4IJRMk.d.cts +0 -17
- package/dist/TaskRegistryClient-Ch6lYqM_.d.ts +0 -24
- package/dist/TaskRegistryClient-CrQjddDc.d.cts +0 -24
- package/dist/aes-J-7HrHze.d.cts +0 -17
- package/dist/aes-J-7HrHze.d.ts +0 -17
- package/dist/agent/index.cjs +0 -425
- package/dist/agent/index.cjs.map +0 -1
- package/dist/agent/index.d.cts +0 -63
- package/dist/agent/index.js.map +0 -1
- package/dist/api/index.cjs +0 -4
- package/dist/api/index.cjs.map +0 -1
- package/dist/api/index.d.cts +0 -2
- package/dist/api/index.js.map +0 -1
- package/dist/chain/index.cjs +0 -2484
- package/dist/chain/index.cjs.map +0 -1
- package/dist/chain/index.d.cts +0 -2305
- package/dist/chain/index.js.map +0 -1
- package/dist/compute/index.cjs +0 -4
- package/dist/compute/index.cjs.map +0 -1
- package/dist/compute/index.d.cts +0 -2
- package/dist/compute/index.js.map +0 -1
- package/dist/crypto/index.cjs +0 -424
- package/dist/crypto/index.cjs.map +0 -1
- package/dist/crypto/index.d.cts +0 -74
- package/dist/crypto/index.js.map +0 -1
- package/dist/errors/BlindBountyError.d.ts +0 -14
- package/dist/errors/BlindBountyError.js +0 -30
- package/dist/errors/index.cjs +0 -136
- package/dist/errors/index.cjs.map +0 -1
- package/dist/errors/index.d.cts +0 -60
- package/dist/errors/index.js.map +0 -1
- package/dist/events/index.cjs +0 -41
- package/dist/events/index.cjs.map +0 -1
- package/dist/events/index.d.cts +0 -61
- package/dist/events/index.js.map +0 -1
- package/dist/index.cjs +0 -4
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -2
- package/dist/index.js.map +0 -1
- package/dist/keystore/index.cjs +0 -335
- package/dist/keystore/index.cjs.map +0 -1
- package/dist/keystore/index.d.cts +0 -23
- package/dist/keystore/index.js.map +0 -1
- package/dist/mcp.d.ts +0 -23
- package/dist/mcp.js +0 -101
- package/dist/network/index.cjs +0 -74
- package/dist/network/index.cjs.map +0 -1
- package/dist/network/index.d.cts +0 -36
- package/dist/network/index.js.map +0 -1
- package/dist/signer/index.cjs +0 -121
- package/dist/signer/index.cjs.map +0 -1
- package/dist/signer/index.d.cts +0 -40
- package/dist/signer/index.js.map +0 -1
- package/dist/storage/ZgStorage.d.ts +0 -24
- package/dist/storage/ZgStorage.js +0 -102
- package/dist/storage/index.cjs +0 -221
- package/dist/storage/index.cjs.map +0 -1
- package/dist/storage/index.d.cts +0 -44
- package/dist/storage/index.js.map +0 -1
- package/dist/testing/index.cjs +0 -4
- package/dist/testing/index.cjs.map +0 -1
- package/dist/testing/index.d.cts +0 -2
- package/dist/testing/index.js.map +0 -1
- package/dist/types-BOG5Ub1z.d.cts +0 -31
- package/dist/types-BOG5Ub1z.d.ts +0 -31
- package/dist/types-V7_ZmUb-.d.cts +0 -19
- package/dist/types-hAsITUQ7.d.ts +0 -19
- package/dist/verifier/index.cjs +0 -4
- package/dist/verifier/index.cjs.map +0 -1
- package/dist/verifier/index.d.cts +0 -2
- package/dist/verifier/index.js.map +0 -1
- package/dist/worker/index.cjs +0 -382
- package/dist/worker/index.cjs.map +0 -1
- package/dist/worker/index.d.cts +0 -58
- package/dist/worker/index.js.map +0 -1
- package/examples/agent-demo.ts +0 -121
- package/scripts/gen-crypto-fixtures.mjs +0 -72
- package/src/agent/Agent.ts +0 -122
- package/src/agent/index.ts +0 -1
- package/src/chain/BlindEscrowClient.ts +0 -187
- package/src/chain/BlindReputationClient.ts +0 -55
- package/src/chain/ChainClient.ts +0 -59
- package/src/chain/TaskRegistryClient.ts +0 -82
- package/src/chain/abi/BlindEscrow.json +0 -1113
- package/src/chain/abi/BlindReputation.json +0 -447
- package/src/chain/abi/TaskRegistry.json +0 -586
- package/src/chain/abi/index.ts +0 -5
- package/src/chain/errors.ts +0 -43
- package/src/chain/index.ts +0 -6
- package/src/crypto/aes.ts +0 -104
- package/src/crypto/constants.ts +0 -7
- package/src/crypto/ecies.ts +0 -60
- package/src/crypto/envelope.ts +0 -131
- package/src/crypto/hkdf.ts +0 -45
- package/src/crypto/index.ts +0 -6
- package/src/crypto/keys.ts +0 -56
- package/src/errors/BlindMarketError.ts +0 -38
- package/src/errors/codes.ts +0 -67
- package/src/errors/index.ts +0 -3
- package/src/errors/subclasses.ts +0 -59
- package/src/events/EventBus.ts +0 -45
- package/src/events/index.ts +0 -2
- package/src/events/types.ts +0 -13
- package/src/index.ts +0 -617
- package/src/keystore/InMemoryKeyStore.ts +0 -146
- package/src/keystore/KeyStore.ts +0 -19
- package/src/keystore/index.ts +0 -2
- package/src/network/index.ts +0 -3
- package/src/network/presets.ts +0 -27
- package/src/network/resolve.ts +0 -19
- package/src/network/types.ts +0 -18
- package/src/signer/EthersSigner.ts +0 -82
- package/src/signer/PrivateKeySigner.ts +0 -19
- package/src/signer/Signer.ts +0 -40
- package/src/signer/index.ts +0 -3
- package/src/storage/MemoryStorage.ts +0 -49
- package/src/storage/Storage.ts +0 -15
- package/src/storage/ZgStorage.ts +0 -124
- package/src/storage/index.ts +0 -3
- package/src/testing/index.ts +0 -1
- package/src/tools/adapters.ts +0 -177
- package/src/tools/helpers.ts +0 -199
- package/src/tools/index.ts +0 -59
- package/src/tools/types.ts +0 -47
- package/src/types.ts +0 -296
- package/src/verifier/index.ts +0 -1
- package/src/worker/Worker.ts +0 -98
- package/src/worker/index.ts +0 -1
- package/test/agent.test.ts +0 -193
- package/test/chain.test.ts +0 -145
- package/test/crypto.aes.test.ts +0 -81
- package/test/crypto.compat.test.ts +0 -33
- package/test/crypto.ecies.test.ts +0 -64
- package/test/envelope.test.ts +0 -82
- package/test/errors.test.ts +0 -64
- package/test/events.test.ts +0 -59
- package/test/fixtures/crypto.json +0 -16
- package/test/keystore.test.ts +0 -103
- package/test/network.test.ts +0 -53
- package/test/signer.test.ts +0 -66
- package/test/smoke.test.ts +0 -7
- package/test/storage.test.ts +0 -55
- package/test/worker.test.ts +0 -181
- package/tsconfig.build.json +0 -1
- package/tsconfig.json +0 -14
- package/tsup.config.ts +0 -32
- package/vitest.config.ts +0 -17
- /package/{src/api/index.ts → dist/chain/IBlindMarketChain.js} +0 -0
- /package/{src/compute/index.ts → dist/chain/domain-types.js} +0 -0
package/test/chain.test.ts
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { ethers } from 'ethers';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import {
|
|
4
|
-
BlindEscrowAbi,
|
|
5
|
-
BlindEscrowClient,
|
|
6
|
-
BlindReputationAbi,
|
|
7
|
-
BlindReputationClient,
|
|
8
|
-
ChainClient,
|
|
9
|
-
TaskRegistryAbi,
|
|
10
|
-
TaskRegistryClient,
|
|
11
|
-
wrapChainError,
|
|
12
|
-
} from '../src/chain/index.js';
|
|
13
|
-
import { networks } from '../src/network/index.js';
|
|
14
|
-
import { PrivateKeySigner } from '../src/signer/index.js';
|
|
15
|
-
import type { Address } from '../src/types.js';
|
|
16
|
-
|
|
17
|
-
const FIXED_PK = '0x0123456789012345678901234567890123456789012345678901234567890123';
|
|
18
|
-
const RAND_ADDR = '0xFd4F93F5A7BE144c405D1D8fbEC63Fb776207681' as Address;
|
|
19
|
-
|
|
20
|
-
describe('ABIs', () => {
|
|
21
|
-
it('BlindEscrow ABI exposes the expected surface', () => {
|
|
22
|
-
const iface = new ethers.Interface(BlindEscrowAbi);
|
|
23
|
-
for (const name of [
|
|
24
|
-
'createTask',
|
|
25
|
-
'assignWorker',
|
|
26
|
-
'submitEvidence',
|
|
27
|
-
'completeVerification',
|
|
28
|
-
'cancelTask',
|
|
29
|
-
'getTask',
|
|
30
|
-
'nextTaskId',
|
|
31
|
-
]) {
|
|
32
|
-
expect(iface.getFunction(name), `missing ${name}`).not.toBeNull();
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('TaskRegistry ABI exposes the expected surface', () => {
|
|
37
|
-
const iface = new ethers.Interface(TaskRegistryAbi);
|
|
38
|
-
for (const name of ['getOpenTasks', 'getTaskMeta', 'totalTasks', 'openTaskCount']) {
|
|
39
|
-
expect(iface.getFunction(name), `missing ${name}`).not.toBeNull();
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('BlindReputation ABI exposes the expected surface', () => {
|
|
44
|
-
const iface = new ethers.Interface(BlindReputationAbi);
|
|
45
|
-
for (const name of ['rate', 'getReputation', 'hasBeenRated']) {
|
|
46
|
-
expect(iface.getFunction(name), `missing ${name}`).not.toBeNull();
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe('encoding round-trips (Interface level, no live RPC)', () => {
|
|
52
|
-
it('BlindEscrow.createTask encodes to a call-data blob matching the ABI', () => {
|
|
53
|
-
const iface = new ethers.Interface(BlindEscrowAbi);
|
|
54
|
-
const data = iface.encodeFunctionData('createTask', [
|
|
55
|
-
'0x' + '11'.repeat(32),
|
|
56
|
-
RAND_ADDR,
|
|
57
|
-
1_000000n,
|
|
58
|
-
'photography',
|
|
59
|
-
'Lagos, NG',
|
|
60
|
-
3600n,
|
|
61
|
-
]);
|
|
62
|
-
const decoded = iface.decodeFunctionData('createTask', data);
|
|
63
|
-
expect(decoded[0]).toBe('0x' + '11'.repeat(32));
|
|
64
|
-
expect((decoded[1] as string).toLowerCase()).toBe(RAND_ADDR.toLowerCase());
|
|
65
|
-
expect(decoded[2]).toBe(1_000000n);
|
|
66
|
-
expect(decoded[3]).toBe('photography');
|
|
67
|
-
expect(decoded[4]).toBe('Lagos, NG');
|
|
68
|
-
expect(decoded[5]).toBe(3600n);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('BlindReputation.rate encodes args correctly', () => {
|
|
72
|
-
const iface = new ethers.Interface(BlindReputationAbi);
|
|
73
|
-
const data = iface.encodeFunctionData('rate', [RAND_ADDR, 5, 42n]);
|
|
74
|
-
const decoded = iface.decodeFunctionData('rate', data);
|
|
75
|
-
expect((decoded[0] as string).toLowerCase()).toBe(RAND_ADDR.toLowerCase());
|
|
76
|
-
expect(Number(decoded[1])).toBe(5);
|
|
77
|
-
expect(decoded[2]).toBe(42n);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
describe('ChainClient wiring', () => {
|
|
82
|
-
it('constructs with a signer and exposes typed sub-clients at the testnet addresses', () => {
|
|
83
|
-
const signer = new PrivateKeySigner(FIXED_PK);
|
|
84
|
-
// No provider attached — reads will fail, but construction should succeed
|
|
85
|
-
// because EthersSigner exposes .provider = null and we fall through to
|
|
86
|
-
// defaultProvider(network) for the RPC runner.
|
|
87
|
-
const c = new ChainClient({ network: networks.testnet, signer });
|
|
88
|
-
expect(c.escrow).toBeInstanceOf(BlindEscrowClient);
|
|
89
|
-
expect(c.registry).toBeInstanceOf(TaskRegistryClient);
|
|
90
|
-
expect(c.reputation).toBeInstanceOf(BlindReputationClient);
|
|
91
|
-
expect(c.escrow.address.toLowerCase()).toBe(networks.testnet.contracts.escrow.toLowerCase());
|
|
92
|
-
expect(c.registry.address.toLowerCase()).toBe(networks.testnet.contracts.registry.toLowerCase());
|
|
93
|
-
expect(c.reputation.address.toLowerCase()).toBe(networks.testnet.contracts.reputation.toLowerCase());
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('constructs read-only with only a provider', () => {
|
|
97
|
-
const provider = new ethers.JsonRpcProvider(networks.testnet.rpc[0], {
|
|
98
|
-
chainId: Number(networks.testnet.chainId),
|
|
99
|
-
name: networks.testnet.name,
|
|
100
|
-
});
|
|
101
|
-
const c = new ChainClient({ network: networks.testnet, provider });
|
|
102
|
-
expect(c.escrow).toBeInstanceOf(BlindEscrowClient);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
describe('wrapChainError', () => {
|
|
107
|
-
it('maps INSUFFICIENT_FUNDS to CHAIN/INSUFFICIENT_BALANCE', () => {
|
|
108
|
-
const err = wrapChainError({ code: 'INSUFFICIENT_FUNDS', message: 'no funds' });
|
|
109
|
-
expect(err.code).toBe('CHAIN/INSUFFICIENT_BALANCE');
|
|
110
|
-
expect(err.retriable).toBe(false);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('maps NONCE_EXPIRED to CHAIN/NONCE_CONFLICT (retriable)', () => {
|
|
114
|
-
const err = wrapChainError({ code: 'NONCE_EXPIRED', message: 'nonce' });
|
|
115
|
-
expect(err.code).toBe('CHAIN/NONCE_CONFLICT');
|
|
116
|
-
expect(err.retriable).toBe(true);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('maps TIMEOUT to CHAIN/TX_TIMED_OUT (retriable)', () => {
|
|
120
|
-
const err = wrapChainError({ code: 'TIMEOUT', message: 't' });
|
|
121
|
-
expect(err.code).toBe('CHAIN/TX_TIMED_OUT');
|
|
122
|
-
expect(err.retriable).toBe(true);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('maps CALL_EXCEPTION to CHAIN/TX_REVERTED and surfaces revert data', () => {
|
|
126
|
-
const err = wrapChainError({
|
|
127
|
-
code: 'CALL_EXCEPTION',
|
|
128
|
-
shortMessage: 'reverted',
|
|
129
|
-
data: '0xdeadbeef',
|
|
130
|
-
});
|
|
131
|
-
expect(err.code).toBe('CHAIN/TX_REVERTED');
|
|
132
|
-
expect(err.context).toEqual({ data: '0xdeadbeef' });
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('defaults unknown errors to CHAIN/TX_REVERTED', () => {
|
|
136
|
-
const err = wrapChainError(new Error('boom'));
|
|
137
|
-
expect(err.code).toBe('CHAIN/TX_REVERTED');
|
|
138
|
-
expect(err.retriable).toBe(false);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('prefixes the hint', () => {
|
|
142
|
-
const err = wrapChainError({ message: 'x' }, 'createTask');
|
|
143
|
-
expect(err.message).toMatch(/^createTask:/);
|
|
144
|
-
});
|
|
145
|
-
});
|
package/test/crypto.aes.test.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import * as fc from 'fast-check';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import { aesDecrypt, aesEncrypt, generateAesKey, sha256 } from '../src/crypto/index.js';
|
|
4
|
-
import { CryptoError } from '../src/errors/index.js';
|
|
5
|
-
|
|
6
|
-
describe('aes-256-gcm', () => {
|
|
7
|
-
it('encrypts then decrypts arbitrary bytes', async () => {
|
|
8
|
-
const key = await generateAesKey();
|
|
9
|
-
const pt = new TextEncoder().encode('hello bounty');
|
|
10
|
-
const ct = await aesEncrypt(pt, key);
|
|
11
|
-
expect(ct.byteLength).toBe(pt.byteLength + 12 + 16);
|
|
12
|
-
const rt = await aesDecrypt(ct, key);
|
|
13
|
-
expect(new TextDecoder().decode(rt)).toBe('hello bounty');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('generates distinct keys each call', async () => {
|
|
17
|
-
const a = await generateAesKey();
|
|
18
|
-
const b = await generateAesKey();
|
|
19
|
-
expect(a.byteLength).toBe(32);
|
|
20
|
-
expect(b.byteLength).toBe(32);
|
|
21
|
-
expect(a).not.toEqual(b);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('emits distinct ciphertexts for the same plaintext+key (random IV)', async () => {
|
|
25
|
-
const key = await generateAesKey();
|
|
26
|
-
const pt = new TextEncoder().encode('same text');
|
|
27
|
-
const c1 = await aesEncrypt(pt, key);
|
|
28
|
-
const c2 = await aesEncrypt(pt, key);
|
|
29
|
-
expect(c1).not.toEqual(c2);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('rejects non-32-byte keys on encrypt', async () => {
|
|
33
|
-
const key = new Uint8Array(16);
|
|
34
|
-
await expect(aesEncrypt(new Uint8Array(4), key)).rejects.toBeInstanceOf(CryptoError);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('rejects non-32-byte keys on decrypt', async () => {
|
|
38
|
-
const key = new Uint8Array(16);
|
|
39
|
-
await expect(aesDecrypt(new Uint8Array(32), key)).rejects.toBeInstanceOf(CryptoError);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('rejects truncated blobs', async () => {
|
|
43
|
-
const key = await generateAesKey();
|
|
44
|
-
await expect(aesDecrypt(new Uint8Array(10), key)).rejects.toBeInstanceOf(CryptoError);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('detects tampering (auth tag check)', async () => {
|
|
48
|
-
const key = await generateAesKey();
|
|
49
|
-
const ct = await aesEncrypt(new TextEncoder().encode('data'), key);
|
|
50
|
-
// biome-ignore lint/style/noNonNullAssertion: we just created the array
|
|
51
|
-
ct[ct.length - 1] = (ct[ct.length - 1]! ^ 0x01) & 0xff;
|
|
52
|
-
await expect(aesDecrypt(ct, key)).rejects.toBeInstanceOf(CryptoError);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('round-trips zero-byte plaintext', async () => {
|
|
56
|
-
const key = await generateAesKey();
|
|
57
|
-
const ct = await aesEncrypt(new Uint8Array(0), key);
|
|
58
|
-
expect(ct.byteLength).toBe(28); // 12 IV + 16 tag + 0 ct
|
|
59
|
-
const rt = await aesDecrypt(ct, key);
|
|
60
|
-
expect(rt.byteLength).toBe(0);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('property: arbitrary plaintext round-trips', async () => {
|
|
64
|
-
await fc.assert(
|
|
65
|
-
fc.asyncProperty(fc.uint8Array({ minLength: 0, maxLength: 4096 }), async (pt) => {
|
|
66
|
-
const key = await generateAesKey();
|
|
67
|
-
const rt = await aesDecrypt(await aesEncrypt(pt, key), key);
|
|
68
|
-
expect(Array.from(rt)).toEqual(Array.from(pt));
|
|
69
|
-
}),
|
|
70
|
-
{ numRuns: 50 },
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe('sha256', () => {
|
|
76
|
-
it('matches a known vector', async () => {
|
|
77
|
-
const digest = await sha256(new TextEncoder().encode('abc'));
|
|
78
|
-
const hex = Array.from(digest, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
79
|
-
expect(hex).toBe('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad');
|
|
80
|
-
});
|
|
81
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wire-format compatibility: the SDK (Web Crypto) must decrypt blobs produced by
|
|
3
|
-
* backend/src/services/crypto.ts (Node crypto). The fixture at test/fixtures/crypto.json
|
|
4
|
-
* is generated by scripts/gen-crypto-fixtures.mjs using the same Node primitives
|
|
5
|
-
* as the backend — if this test fails, the SDK has diverged from the backend's
|
|
6
|
-
* byte layout and will break cross-stack encryption.
|
|
7
|
-
*/
|
|
8
|
-
import { describe, expect, it } from 'vitest';
|
|
9
|
-
import fixture from './fixtures/crypto.json' with { type: 'json' };
|
|
10
|
-
import { aesDecrypt, eciesDecrypt } from '../src/crypto/index.js';
|
|
11
|
-
|
|
12
|
-
function hexToBytes(hex: string): Uint8Array {
|
|
13
|
-
const out = new Uint8Array(hex.length / 2);
|
|
14
|
-
for (let i = 0; i < out.length; i++) {
|
|
15
|
-
out[i] = Number.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
|
|
16
|
-
}
|
|
17
|
-
return out;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
describe('crypto cross-compat with backend wire format', () => {
|
|
21
|
-
it('SDK decrypts backend-produced AES blob', async () => {
|
|
22
|
-
const key = hexToBytes(fixture.aes.key);
|
|
23
|
-
const blob = hexToBytes(fixture.aes.blob);
|
|
24
|
-
const pt = await aesDecrypt(blob, key);
|
|
25
|
-
expect(new TextDecoder().decode(pt)).toBe(fixture.plaintext);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('SDK decrypts backend-produced ECIES blob', async () => {
|
|
29
|
-
const blob = hexToBytes(fixture.ecies.blob);
|
|
30
|
-
const pt = await eciesDecrypt(blob, fixture.keypair.privateKey);
|
|
31
|
-
expect(new TextDecoder().decode(pt)).toBe(fixture.plaintext);
|
|
32
|
-
});
|
|
33
|
-
});
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import * as fc from 'fast-check';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import {
|
|
4
|
-
derivePublicKey,
|
|
5
|
-
eciesDecrypt,
|
|
6
|
-
eciesEncrypt,
|
|
7
|
-
generateKeyPair,
|
|
8
|
-
} from '../src/crypto/index.js';
|
|
9
|
-
import { CryptoError } from '../src/errors/index.js';
|
|
10
|
-
|
|
11
|
-
describe('ecies secp256k1', () => {
|
|
12
|
-
it('round-trips', async () => {
|
|
13
|
-
const { privateKey, publicKey } = generateKeyPair();
|
|
14
|
-
const pt = new TextEncoder().encode('secret instructions');
|
|
15
|
-
const ct = await eciesEncrypt(pt, publicKey);
|
|
16
|
-
const rt = await eciesDecrypt(ct, privateKey);
|
|
17
|
-
expect(new TextDecoder().decode(rt)).toBe('secret instructions');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('wire format: [65 ephemeral pubkey][12 IV][16 tag][ciphertext]', async () => {
|
|
21
|
-
const { publicKey } = generateKeyPair();
|
|
22
|
-
const ct = await eciesEncrypt(new Uint8Array([1, 2, 3]), publicKey);
|
|
23
|
-
expect(ct.byteLength).toBe(65 + 12 + 16 + 3);
|
|
24
|
-
// First byte of ephemeral pubkey must be 0x04 (uncompressed marker)
|
|
25
|
-
expect(ct[0]).toBe(0x04);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('wrong private key fails', async () => {
|
|
29
|
-
const good = generateKeyPair();
|
|
30
|
-
const bad = generateKeyPair();
|
|
31
|
-
const ct = await eciesEncrypt(new Uint8Array([9]), good.publicKey);
|
|
32
|
-
await expect(eciesDecrypt(ct, bad.privateKey)).rejects.toBeInstanceOf(CryptoError);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('rejects truncated blobs', async () => {
|
|
36
|
-
const { privateKey } = generateKeyPair();
|
|
37
|
-
await expect(eciesDecrypt(new Uint8Array(30), privateKey)).rejects.toBeInstanceOf(CryptoError);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('rejects compressed / malformed recipient pubkey', async () => {
|
|
41
|
-
const bogus = '02' + '00'.repeat(32); // compressed form (33 bytes), not supported
|
|
42
|
-
await expect(eciesEncrypt(new Uint8Array([0]), bogus)).rejects.toBeInstanceOf(CryptoError);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('derivePublicKey recovers the same pubkey used for encryption', async () => {
|
|
46
|
-
const { privateKey, publicKey } = generateKeyPair();
|
|
47
|
-
const recovered = derivePublicKey(privateKey);
|
|
48
|
-
expect(recovered.toLowerCase()).toBe(publicKey.toLowerCase());
|
|
49
|
-
const pt = new TextEncoder().encode('x');
|
|
50
|
-
const ct = await eciesEncrypt(pt, recovered);
|
|
51
|
-
expect(new TextDecoder().decode(await eciesDecrypt(ct, privateKey))).toBe('x');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('property: arbitrary plaintext round-trips', async () => {
|
|
55
|
-
await fc.assert(
|
|
56
|
-
fc.asyncProperty(fc.uint8Array({ minLength: 0, maxLength: 1024 }), async (pt) => {
|
|
57
|
-
const { privateKey, publicKey } = generateKeyPair();
|
|
58
|
-
const rt = await eciesDecrypt(await eciesEncrypt(pt, publicKey), privateKey);
|
|
59
|
-
expect(Array.from(rt)).toEqual(Array.from(pt));
|
|
60
|
-
}),
|
|
61
|
-
{ numRuns: 25 },
|
|
62
|
-
);
|
|
63
|
-
});
|
|
64
|
-
});
|
package/test/envelope.test.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
aesDecrypt,
|
|
4
|
-
eciesDecrypt,
|
|
5
|
-
generateKeyPair,
|
|
6
|
-
openEvidenceAsAgent,
|
|
7
|
-
sealEvidence,
|
|
8
|
-
__internals,
|
|
9
|
-
} from '../src/crypto/index.js';
|
|
10
|
-
import { CryptoError } from '../src/errors/index.js';
|
|
11
|
-
|
|
12
|
-
describe('evidence envelope', () => {
|
|
13
|
-
it('seal → agent-open round-trips', async () => {
|
|
14
|
-
const agent = generateKeyPair();
|
|
15
|
-
const enclave = generateKeyPair();
|
|
16
|
-
const evidence = new TextEncoder().encode('I did the photography task');
|
|
17
|
-
const { envelope } = await sealEvidence({
|
|
18
|
-
evidence,
|
|
19
|
-
agentPubKey: agent.publicKey,
|
|
20
|
-
enclavePubKey: enclave.publicKey,
|
|
21
|
-
});
|
|
22
|
-
const recovered = await openEvidenceAsAgent(envelope, agent.privateKey);
|
|
23
|
-
expect(new TextDecoder().decode(recovered)).toBe('I did the photography task');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('enclave-wrapped key also decrypts to the same AES key', async () => {
|
|
27
|
-
const agent = generateKeyPair();
|
|
28
|
-
const enclave = generateKeyPair();
|
|
29
|
-
const evidence = new TextEncoder().encode('enclave-path');
|
|
30
|
-
const { envelope, aesKey } = await sealEvidence({
|
|
31
|
-
evidence,
|
|
32
|
-
agentPubKey: agent.publicKey,
|
|
33
|
-
enclavePubKey: enclave.publicKey,
|
|
34
|
-
});
|
|
35
|
-
const parsed = __internals.parse(envelope);
|
|
36
|
-
const keyFromEnclave = await eciesDecrypt(parsed.wrappedForEnclave, enclave.privateKey);
|
|
37
|
-
const keyFromAgent = await eciesDecrypt(parsed.wrappedForAgent, agent.privateKey);
|
|
38
|
-
expect(Array.from(keyFromEnclave)).toEqual(Array.from(aesKey));
|
|
39
|
-
expect(Array.from(keyFromAgent)).toEqual(Array.from(aesKey));
|
|
40
|
-
// And the key decrypts the AES portion independently
|
|
41
|
-
const pt = await aesDecrypt(parsed.aesCiphertext, keyFromEnclave);
|
|
42
|
-
expect(new TextDecoder().decode(pt)).toBe('enclave-path');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('wrong magic bytes throw CRYPTO/INTEGRITY_CHECK', async () => {
|
|
46
|
-
const agent = generateKeyPair();
|
|
47
|
-
const enclave = generateKeyPair();
|
|
48
|
-
const { envelope } = await sealEvidence({
|
|
49
|
-
evidence: new Uint8Array([1, 2, 3]),
|
|
50
|
-
agentPubKey: agent.publicKey,
|
|
51
|
-
enclavePubKey: enclave.publicKey,
|
|
52
|
-
});
|
|
53
|
-
envelope[0] = 0xff;
|
|
54
|
-
await expect(openEvidenceAsAgent(envelope, agent.privateKey)).rejects.toBeInstanceOf(CryptoError);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('unknown version throws CRYPTO/UNSUPPORTED_CIPHER', async () => {
|
|
58
|
-
const agent = generateKeyPair();
|
|
59
|
-
const enclave = generateKeyPair();
|
|
60
|
-
const { envelope } = await sealEvidence({
|
|
61
|
-
evidence: new Uint8Array([1, 2, 3]),
|
|
62
|
-
agentPubKey: agent.publicKey,
|
|
63
|
-
enclavePubKey: enclave.publicKey,
|
|
64
|
-
});
|
|
65
|
-
envelope[2] = 0x99;
|
|
66
|
-
await expect(openEvidenceAsAgent(envelope, agent.privateKey)).rejects.toMatchObject({
|
|
67
|
-
code: 'CRYPTO/UNSUPPORTED_CIPHER',
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('truncated envelope throws CRYPTO/INTEGRITY_CHECK', async () => {
|
|
72
|
-
const agent = generateKeyPair();
|
|
73
|
-
const enclave = generateKeyPair();
|
|
74
|
-
const { envelope } = await sealEvidence({
|
|
75
|
-
evidence: new Uint8Array([1, 2, 3]),
|
|
76
|
-
agentPubKey: agent.publicKey,
|
|
77
|
-
enclavePubKey: enclave.publicKey,
|
|
78
|
-
});
|
|
79
|
-
const short = envelope.slice(0, 10);
|
|
80
|
-
await expect(openEvidenceAsAgent(short, agent.privateKey)).rejects.toBeInstanceOf(CryptoError);
|
|
81
|
-
});
|
|
82
|
-
});
|
package/test/errors.test.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
BlindMarketError,
|
|
4
|
-
ConfigError,
|
|
5
|
-
CryptoError,
|
|
6
|
-
StorageError,
|
|
7
|
-
ChainError,
|
|
8
|
-
ComputeError,
|
|
9
|
-
ApiError,
|
|
10
|
-
LifecycleError,
|
|
11
|
-
TimeoutError,
|
|
12
|
-
} from '../src/errors/index.js';
|
|
13
|
-
|
|
14
|
-
describe('BlindMarketError', () => {
|
|
15
|
-
it('preserves code, message, retriable, context, cause', () => {
|
|
16
|
-
const cause = new Error('root');
|
|
17
|
-
const err = new StorageError('STORAGE/UPLOAD_FAILED', 'upload blew up', {
|
|
18
|
-
retriable: true,
|
|
19
|
-
context: { attempt: 1 },
|
|
20
|
-
cause,
|
|
21
|
-
});
|
|
22
|
-
expect(err).toBeInstanceOf(BlindMarketError);
|
|
23
|
-
expect(err.name).toBe('StorageError');
|
|
24
|
-
expect(err.code).toBe('STORAGE/UPLOAD_FAILED');
|
|
25
|
-
expect(err.message).toBe('upload blew up');
|
|
26
|
-
expect(err.retriable).toBe(true);
|
|
27
|
-
expect(err.context).toEqual({ attempt: 1 });
|
|
28
|
-
expect(err.cause).toBe(cause);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('defaults retriable to false', () => {
|
|
32
|
-
const err = new ConfigError('CONFIG/MISSING_SIGNER', 'no signer');
|
|
33
|
-
expect(err.retriable).toBe(false);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('exposes a stable toJSON for telemetry (no secrets)', () => {
|
|
37
|
-
const err = new ApiError('API/VALIDATION', 'bad input', { context: { field: 'reward' } });
|
|
38
|
-
const j = err.toJSON();
|
|
39
|
-
expect(j).toMatchObject({
|
|
40
|
-
name: 'ApiError',
|
|
41
|
-
code: 'API/VALIDATION',
|
|
42
|
-
message: 'bad input',
|
|
43
|
-
retriable: false,
|
|
44
|
-
context: { field: 'reward' },
|
|
45
|
-
});
|
|
46
|
-
expect(j).not.toHaveProperty('cause');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it.each([
|
|
50
|
-
[ConfigError, 'CONFIG/MISSING_SIGNER'],
|
|
51
|
-
[CryptoError, 'CRYPTO/DECRYPT_FAILED'],
|
|
52
|
-
[StorageError, 'STORAGE/NOT_FOUND'],
|
|
53
|
-
[ChainError, 'CHAIN/TX_REVERTED'],
|
|
54
|
-
[ComputeError, 'COMPUTE/BROKER_UNREACHABLE'],
|
|
55
|
-
[ApiError, 'API/RATE_LIMITED'],
|
|
56
|
-
[LifecycleError, 'LIFECYCLE/ILLEGAL_TRANSITION'],
|
|
57
|
-
[TimeoutError, 'TIMEOUT'],
|
|
58
|
-
] as const)('subclass %p accepts its canonical code', (Ctor, code) => {
|
|
59
|
-
// biome-ignore lint/suspicious/noExplicitAny: dynamic ctor call for table test
|
|
60
|
-
const err = new (Ctor as any)(code, 'msg');
|
|
61
|
-
expect(err.code).toBe(code);
|
|
62
|
-
expect(err).toBeInstanceOf(BlindMarketError);
|
|
63
|
-
});
|
|
64
|
-
});
|
package/test/events.test.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { EventBus, type SdkEvents } from '../src/events/index.js';
|
|
3
|
-
|
|
4
|
-
const txPayload = { hash: '0xabc' as `0x${string}`, from: '0x00' as `0x${string}`, to: '0x01' as `0x${string}` };
|
|
5
|
-
|
|
6
|
-
describe('EventBus', () => {
|
|
7
|
-
it('delivers typed events to subscribers', () => {
|
|
8
|
-
const bus = new EventBus<SdkEvents>();
|
|
9
|
-
const fn = vi.fn();
|
|
10
|
-
bus.on('tx.sent', fn);
|
|
11
|
-
bus.emit('tx.sent', txPayload);
|
|
12
|
-
expect(fn).toHaveBeenCalledWith(txPayload);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('once fires only once', () => {
|
|
16
|
-
const bus = new EventBus<SdkEvents>();
|
|
17
|
-
const fn = vi.fn();
|
|
18
|
-
bus.once('tx.sent', fn);
|
|
19
|
-
bus.emit('tx.sent', txPayload);
|
|
20
|
-
bus.emit('tx.sent', txPayload);
|
|
21
|
-
expect(fn).toHaveBeenCalledTimes(1);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('off removes a specific listener', () => {
|
|
25
|
-
const bus = new EventBus<SdkEvents>();
|
|
26
|
-
const fn = vi.fn();
|
|
27
|
-
bus.on('tx.sent', fn);
|
|
28
|
-
bus.off('tx.sent', fn);
|
|
29
|
-
bus.emit('tx.sent', txPayload);
|
|
30
|
-
expect(fn).not.toHaveBeenCalled();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('on returns an unsubscribe function', () => {
|
|
34
|
-
const bus = new EventBus<SdkEvents>();
|
|
35
|
-
const fn = vi.fn();
|
|
36
|
-
const unsubscribe = bus.on('tx.sent', fn);
|
|
37
|
-
unsubscribe();
|
|
38
|
-
bus.emit('tx.sent', txPayload);
|
|
39
|
-
expect(fn).not.toHaveBeenCalled();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('isolates listener exceptions (all other listeners still fire)', () => {
|
|
43
|
-
const bus = new EventBus<SdkEvents>();
|
|
44
|
-
const bad = vi.fn(() => {
|
|
45
|
-
throw new Error('boom');
|
|
46
|
-
});
|
|
47
|
-
const good = vi.fn();
|
|
48
|
-
bus.on('tx.sent', bad);
|
|
49
|
-
bus.on('tx.sent', good);
|
|
50
|
-
bus.emit('tx.sent', txPayload);
|
|
51
|
-
expect(bad).toHaveBeenCalled();
|
|
52
|
-
expect(good).toHaveBeenCalled();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('emit with no subscribers is a no-op', () => {
|
|
56
|
-
const bus = new EventBus<SdkEvents>();
|
|
57
|
-
expect(() => bus.emit('tx.sent', txPayload)).not.toThrow();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"_note": "Generated by scripts/gen-crypto-fixtures.mjs using Node built-in crypto, matching backend/src/services/crypto.ts. If the SDK decrypts these back to the expected plaintext, wire-format compatibility is proven. Regenerate only on intentional format changes.",
|
|
3
|
-
"hkdf_info": "BlindMarket-ECIES-v1",
|
|
4
|
-
"keypair": {
|
|
5
|
-
"privateKey": "0d1ab8008ec2d77282500acb8e2c8cb237a64e0d2a474ab8f2a8415723e5fb87",
|
|
6
|
-
"publicKey": "04f797b44d169c83d53b444ff690df5233616f4d93bb234e49c48a84d8b256e8291701bdcf4fc608aa987029ddad03957957f97e2cc408a4e9f1fa7d8286cdbcaf"
|
|
7
|
-
},
|
|
8
|
-
"plaintext": "hello cross-compat",
|
|
9
|
-
"aes": {
|
|
10
|
-
"key": "5516bb862924cf891c6071c11a07b7286e149b4886db5beb392cca1181f565d9",
|
|
11
|
-
"blob": "70baf194291fcac086eb0e0ce5cce9a7af3ec485776d1605aaa7732cf5e15b5d4fbd79a4549b44ec9dd266462619"
|
|
12
|
-
},
|
|
13
|
-
"ecies": {
|
|
14
|
-
"blob": "0480f4dbacb4a2bb8575b9c7a7e5f5d47f6f8dc9c804a8d01cb3e9f7101e7040f5937dee7d7e8787033af1259e00d877ab785dcd89f8fc79d62f8f4bf6b51fac8ea72518b2d7845ca6c054e7fd152774852583fca8b71185f418ce6aa29d6d10a7d51c85d02dcfbaeea534d346c27c"
|
|
15
|
-
}
|
|
16
|
-
}
|
package/test/keystore.test.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { generateAesKey } from '../src/crypto/index.js';
|
|
3
|
-
import { CryptoError } from '../src/errors/index.js';
|
|
4
|
-
import { InMemoryKeyStore } from '../src/keystore/index.js';
|
|
5
|
-
import type { Address, Hex, TaskKey } from '../src/types.js';
|
|
6
|
-
|
|
7
|
-
async function mkKey(): Promise<TaskKey> {
|
|
8
|
-
return { aesKey: await generateAesKey(), createdAt: new Date() };
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
describe('InMemoryKeyStore', () => {
|
|
12
|
-
it('round-trips task keys', async () => {
|
|
13
|
-
const ks = new InMemoryKeyStore();
|
|
14
|
-
const tk = await mkKey();
|
|
15
|
-
await ks.putTaskKey(42n, tk);
|
|
16
|
-
const got = await ks.getTaskKey(42n);
|
|
17
|
-
expect(got).not.toBeNull();
|
|
18
|
-
expect(got?.aesKey).toEqual(tk.aesKey);
|
|
19
|
-
expect(got?.createdAt.getTime()).toBe(tk.createdAt.getTime());
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('returns null for missing task keys', async () => {
|
|
23
|
-
const ks = new InMemoryKeyStore();
|
|
24
|
-
expect(await ks.getTaskKey(999n)).toBeNull();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('list enumerates stored task IDs', async () => {
|
|
28
|
-
const ks = new InMemoryKeyStore();
|
|
29
|
-
await ks.putTaskKey(1n, await mkKey());
|
|
30
|
-
await ks.putTaskKey(2n, await mkKey());
|
|
31
|
-
const all = await ks.list();
|
|
32
|
-
expect(all.map((r) => r.taskId).sort()).toEqual([1n, 2n]);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('delete removes a task key', async () => {
|
|
36
|
-
const ks = new InMemoryKeyStore();
|
|
37
|
-
await ks.putTaskKey(7n, await mkKey());
|
|
38
|
-
await ks.delete(7n);
|
|
39
|
-
expect(await ks.getTaskKey(7n)).toBeNull();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('peer pubkey cache', async () => {
|
|
43
|
-
const ks = new InMemoryKeyStore();
|
|
44
|
-
const addr = '0x0000000000000000000000000000000000000001' as Address;
|
|
45
|
-
const pub = ('0x04' + '11'.repeat(64)) as Hex;
|
|
46
|
-
expect(await ks.getPeerPubKey(addr)).toBeNull();
|
|
47
|
-
await ks.putPeerPubKey(addr, pub);
|
|
48
|
-
expect(await ks.getPeerPubKey(addr)).toBe(pub);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('returned AES key is a copy (mutations do not leak back)', async () => {
|
|
52
|
-
const ks = new InMemoryKeyStore();
|
|
53
|
-
await ks.putTaskKey(1n, await mkKey());
|
|
54
|
-
const a = await ks.getTaskKey(1n);
|
|
55
|
-
const b = await ks.getTaskKey(1n);
|
|
56
|
-
// biome-ignore lint/style/noNonNullAssertion: returned by an earlier non-null assertion
|
|
57
|
-
a!.aesKey[0] = (a!.aesKey[0]! ^ 0xff) & 0xff;
|
|
58
|
-
expect(b?.aesKey).not.toEqual(a?.aesKey);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('export/import round-trips with the same passphrase', async () => {
|
|
62
|
-
const src = new InMemoryKeyStore();
|
|
63
|
-
await src.putTaskKey(1n, await mkKey());
|
|
64
|
-
await src.putTaskKey(2n, await mkKey());
|
|
65
|
-
const addr = '0x0000000000000000000000000000000000000009' as Address;
|
|
66
|
-
const pub = ('0x04' + 'ab'.repeat(64)) as Hex;
|
|
67
|
-
await src.putPeerPubKey(addr, pub);
|
|
68
|
-
|
|
69
|
-
const exported = await src.export('correct-horse-battery-staple');
|
|
70
|
-
const dst = new InMemoryKeyStore();
|
|
71
|
-
await dst.import(exported, 'correct-horse-battery-staple');
|
|
72
|
-
|
|
73
|
-
const srcList = (await src.list()).map((r) => r.taskId).sort();
|
|
74
|
-
const dstList = (await dst.list()).map((r) => r.taskId).sort();
|
|
75
|
-
expect(dstList).toEqual(srcList);
|
|
76
|
-
expect((await dst.getTaskKey(1n))?.aesKey).toEqual((await src.getTaskKey(1n))?.aesKey);
|
|
77
|
-
expect(await dst.getPeerPubKey(addr)).toBe(pub);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('import with wrong passphrase fails', async () => {
|
|
81
|
-
const src = new InMemoryKeyStore();
|
|
82
|
-
await src.putTaskKey(1n, await mkKey());
|
|
83
|
-
const exported = await src.export('goodpassphrase');
|
|
84
|
-
const dst = new InMemoryKeyStore();
|
|
85
|
-
await expect(dst.import(exported, 'wrongpassphrase')).rejects.toBeInstanceOf(CryptoError);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('export rejects short passphrases', async () => {
|
|
89
|
-
const ks = new InMemoryKeyStore();
|
|
90
|
-
await expect(ks.export('short')).rejects.toBeInstanceOf(CryptoError);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('import rejects invalid JSON', async () => {
|
|
94
|
-
const ks = new InMemoryKeyStore();
|
|
95
|
-
await expect(ks.import('not-json', 'any-passphrase')).rejects.toBeInstanceOf(CryptoError);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('import rejects unknown format', async () => {
|
|
99
|
-
const ks = new InMemoryKeyStore();
|
|
100
|
-
const fake = JSON.stringify({ format: 'other-format', salt: '00', blob: '00' });
|
|
101
|
-
await expect(ks.import(fake, 'any-passphrase')).rejects.toBeInstanceOf(CryptoError);
|
|
102
|
-
});
|
|
103
|
-
});
|