@cofhe/sdk 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/adapters/ethers5.test.ts +174 -0
- package/adapters/ethers5.ts +36 -0
- package/adapters/ethers6.test.ts +169 -0
- package/adapters/ethers6.ts +36 -0
- package/adapters/hardhat-node.ts +167 -0
- package/adapters/hardhat.hh2.test.ts +159 -0
- package/adapters/hardhat.ts +36 -0
- package/adapters/index.test.ts +20 -0
- package/adapters/index.ts +5 -0
- package/adapters/smartWallet.ts +99 -0
- package/adapters/test-utils.ts +53 -0
- package/adapters/types.ts +6 -0
- package/adapters/wagmi.test.ts +156 -0
- package/adapters/wagmi.ts +17 -0
- package/chains/chains/arbSepolia.ts +14 -0
- package/chains/chains/baseSepolia.ts +14 -0
- package/chains/chains/hardhat.ts +15 -0
- package/chains/chains/localcofhe.ts +14 -0
- package/chains/chains/sepolia.ts +14 -0
- package/chains/chains.test.ts +50 -0
- package/chains/defineChain.ts +18 -0
- package/chains/index.ts +35 -0
- package/chains/types.ts +32 -0
- package/core/baseBuilder.ts +119 -0
- package/core/client.test.ts +315 -0
- package/core/client.ts +292 -0
- package/core/clientTypes.ts +108 -0
- package/core/config.test.ts +235 -0
- package/core/config.ts +220 -0
- package/core/decrypt/MockQueryDecrypterAbi.ts +129 -0
- package/core/decrypt/cofheMocksSealOutput.ts +57 -0
- package/core/decrypt/decryptHandleBuilder.ts +287 -0
- package/core/decrypt/decryptUtils.ts +28 -0
- package/core/decrypt/tnSealOutputV1.ts +59 -0
- package/core/decrypt/tnSealOutputV2.ts +298 -0
- package/core/encrypt/MockZkVerifierAbi.ts +106 -0
- package/core/encrypt/cofheMocksZkVerifySign.ts +284 -0
- package/core/encrypt/encryptInputsBuilder.test.ts +751 -0
- package/core/encrypt/encryptInputsBuilder.ts +560 -0
- package/core/encrypt/encryptUtils.ts +67 -0
- package/core/encrypt/zkPackProveVerify.ts +335 -0
- package/core/error.ts +168 -0
- package/core/fetchKeys.test.ts +195 -0
- package/core/fetchKeys.ts +144 -0
- package/core/index.ts +89 -0
- package/core/keyStore.test.ts +226 -0
- package/core/keyStore.ts +154 -0
- package/core/permits.test.ts +494 -0
- package/core/permits.ts +200 -0
- package/core/types.ts +398 -0
- package/core/utils.ts +130 -0
- package/dist/adapters.cjs +88 -0
- package/dist/adapters.d.cts +14576 -0
- package/dist/adapters.d.ts +14576 -0
- package/dist/adapters.js +83 -0
- package/dist/chains.cjs +114 -0
- package/dist/chains.d.cts +121 -0
- package/dist/chains.d.ts +121 -0
- package/dist/chains.js +1 -0
- package/dist/chunk-UGBVZNRT.js +818 -0
- package/dist/chunk-WEAZ25JO.js +105 -0
- package/dist/chunk-WGCRJCBR.js +2523 -0
- package/dist/clientTypes-5_1nwtUe.d.cts +914 -0
- package/dist/clientTypes-Es7fyi65.d.ts +914 -0
- package/dist/core.cjs +3414 -0
- package/dist/core.d.cts +111 -0
- package/dist/core.d.ts +111 -0
- package/dist/core.js +3 -0
- package/dist/node.cjs +3286 -0
- package/dist/node.d.cts +22 -0
- package/dist/node.d.ts +22 -0
- package/dist/node.js +91 -0
- package/dist/permit-fUSe6KKq.d.cts +349 -0
- package/dist/permit-fUSe6KKq.d.ts +349 -0
- package/dist/permits.cjs +871 -0
- package/dist/permits.d.cts +1045 -0
- package/dist/permits.d.ts +1045 -0
- package/dist/permits.js +1 -0
- package/dist/types-KImPrEIe.d.cts +48 -0
- package/dist/types-KImPrEIe.d.ts +48 -0
- package/dist/web.cjs +3478 -0
- package/dist/web.d.cts +38 -0
- package/dist/web.d.ts +38 -0
- package/dist/web.js +240 -0
- package/dist/zkProve.worker.cjs +93 -0
- package/dist/zkProve.worker.d.cts +2 -0
- package/dist/zkProve.worker.d.ts +2 -0
- package/dist/zkProve.worker.js +91 -0
- package/node/client.test.ts +147 -0
- package/node/config.test.ts +68 -0
- package/node/encryptInputs.test.ts +155 -0
- package/node/index.ts +97 -0
- package/node/storage.ts +51 -0
- package/package.json +27 -15
- package/permits/index.ts +68 -0
- package/permits/localstorage.test.ts +117 -0
- package/permits/permit.test.ts +477 -0
- package/permits/permit.ts +405 -0
- package/permits/sealing.test.ts +84 -0
- package/permits/sealing.ts +131 -0
- package/permits/signature.ts +79 -0
- package/permits/store.test.ts +128 -0
- package/permits/store.ts +166 -0
- package/permits/test-utils.ts +20 -0
- package/permits/types.ts +191 -0
- package/permits/utils.ts +62 -0
- package/permits/validation.test.ts +288 -0
- package/permits/validation.ts +369 -0
- package/web/client.web.test.ts +147 -0
- package/web/config.web.test.ts +69 -0
- package/web/encryptInputs.web.test.ts +172 -0
- package/web/index.ts +161 -0
- package/web/storage.ts +34 -0
- package/web/worker.builder.web.test.ts +148 -0
- package/web/worker.config.web.test.ts +329 -0
- package/web/worker.output.web.test.ts +84 -0
- package/web/workerManager.test.ts +80 -0
- package/web/workerManager.ts +214 -0
- package/web/workerManager.web.test.ts +114 -0
- package/web/zkProve.worker.ts +133 -0
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { EncryptInputsBuilder } from './encryptInputsBuilder.js';
|
|
3
|
+
import {
|
|
4
|
+
type EncryptableItem,
|
|
5
|
+
FheTypes,
|
|
6
|
+
Encryptable,
|
|
7
|
+
type EncryptableUint128,
|
|
8
|
+
EncryptStep,
|
|
9
|
+
type TfheInitializer,
|
|
10
|
+
} from '../types.js';
|
|
11
|
+
import { CofhesdkError, CofhesdkErrorCode } from '../error.js';
|
|
12
|
+
import { fromHexString, toHexString } from '../utils.js';
|
|
13
|
+
import { type PublicClient, createPublicClient, http, type WalletClient, createWalletClient } from 'viem';
|
|
14
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
15
|
+
import { arbitrumSepolia } from 'viem/chains';
|
|
16
|
+
import { type CofhesdkConfig, createCofhesdkConfigBase } from '../config.js';
|
|
17
|
+
import { type ZkBuilderAndCrsGenerator } from './zkPackProveVerify.js';
|
|
18
|
+
import { type KeysStorage, createKeysStore } from '../keyStore.js';
|
|
19
|
+
import { type FheKeyDeserializer } from '../fetchKeys.js';
|
|
20
|
+
|
|
21
|
+
const MockZkVerifierUrl = 'http://localhost:3001';
|
|
22
|
+
|
|
23
|
+
// Test private keys (well-known test keys from Anvil/Hardhat)
|
|
24
|
+
const BOB_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; // Bob - always issuer
|
|
25
|
+
|
|
26
|
+
// Create real viem clients for Arbitrum Sepolia
|
|
27
|
+
const publicClient: PublicClient = createPublicClient({
|
|
28
|
+
chain: arbitrumSepolia,
|
|
29
|
+
transport: http(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const bobWalletClient: WalletClient = createWalletClient({
|
|
33
|
+
chain: arbitrumSepolia,
|
|
34
|
+
transport: http(),
|
|
35
|
+
account: privateKeyToAccount(BOB_PRIVATE_KEY),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const stringifyWithBigInt = (obj: any): string => JSON.stringify(obj, (_, v) => (typeof v === 'bigint' ? `${v}n` : v));
|
|
39
|
+
|
|
40
|
+
const parseWithBigInt = (str: string): any =>
|
|
41
|
+
JSON.parse(str, (_, v) => {
|
|
42
|
+
if (typeof v === 'string' && /^\d+n$/.test(v)) {
|
|
43
|
+
return BigInt(v.slice(0, -1));
|
|
44
|
+
}
|
|
45
|
+
return v;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// packMetadata function removed as it's no longer needed
|
|
49
|
+
const unpackMetadata = (metadata: string) => {
|
|
50
|
+
const [signer, securityZone, chainId] = metadata.split('-');
|
|
51
|
+
return { signer, securityZone: parseInt(securityZone), chainId: parseInt(chainId) };
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const deconstructZkPoKMetadata = (
|
|
55
|
+
metadata: Uint8Array
|
|
56
|
+
): { accountAddr: string; securityZone: number; chainId: number } => {
|
|
57
|
+
if (metadata.length < 53) {
|
|
58
|
+
// 1 + 20 + 32 = 53 bytes minimum
|
|
59
|
+
throw new CofhesdkError({
|
|
60
|
+
code: CofhesdkErrorCode.InternalError,
|
|
61
|
+
message: 'Invalid metadata: insufficient length',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Extract security zone (first byte)
|
|
66
|
+
const securityZone = metadata[0];
|
|
67
|
+
|
|
68
|
+
// Extract account address (next 20 bytes)
|
|
69
|
+
const accountBytes = metadata.slice(1, 21);
|
|
70
|
+
const accountAddr = '0x' + toHexString(accountBytes);
|
|
71
|
+
|
|
72
|
+
// Extract chain ID (next 32 bytes, big-endian u256)
|
|
73
|
+
const chainIdBytes = metadata.slice(21, 53);
|
|
74
|
+
|
|
75
|
+
// Convert from big-endian u256 to number
|
|
76
|
+
let chainId = 0;
|
|
77
|
+
for (let i = 0; i < 32; i++) {
|
|
78
|
+
chainId = (chainId << 8) | chainIdBytes[i];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
accountAddr,
|
|
83
|
+
securityZone,
|
|
84
|
+
chainId,
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
class MockZkListBuilder {
|
|
89
|
+
private items: EncryptableItem[];
|
|
90
|
+
constructor(items: EncryptableItem[] = []) {
|
|
91
|
+
this.items = items;
|
|
92
|
+
}
|
|
93
|
+
push_boolean(data: boolean): void {
|
|
94
|
+
this.items.push({ utype: FheTypes.Bool, data, securityZone: 0 });
|
|
95
|
+
}
|
|
96
|
+
push_u8(data: number): void {
|
|
97
|
+
this.items.push({ utype: FheTypes.Uint8, data: BigInt(data), securityZone: 0 });
|
|
98
|
+
}
|
|
99
|
+
push_u16(data: number): void {
|
|
100
|
+
this.items.push({ utype: FheTypes.Uint16, data: BigInt(data), securityZone: 0 });
|
|
101
|
+
}
|
|
102
|
+
push_u32(data: number): void {
|
|
103
|
+
this.items.push({ utype: FheTypes.Uint32, data: BigInt(data), securityZone: 0 });
|
|
104
|
+
}
|
|
105
|
+
push_u64(data: bigint): void {
|
|
106
|
+
this.items.push({ utype: FheTypes.Uint64, data, securityZone: 0 });
|
|
107
|
+
}
|
|
108
|
+
push_u128(data: bigint): void {
|
|
109
|
+
this.items.push({ utype: FheTypes.Uint128, data, securityZone: 0 });
|
|
110
|
+
}
|
|
111
|
+
push_u160(data: bigint): void {
|
|
112
|
+
this.items.push({ utype: FheTypes.Uint160, data, securityZone: 0 });
|
|
113
|
+
}
|
|
114
|
+
build_with_proof_packed(_crs: any, metadata: Uint8Array, _computeLoad: 1): MockZkProvenList {
|
|
115
|
+
// Clear items to prevent persisting items between tests
|
|
116
|
+
const returnItems = this.items;
|
|
117
|
+
this.items = [];
|
|
118
|
+
|
|
119
|
+
return new MockZkProvenList(returnItems, metadata);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const MockCrs = {
|
|
124
|
+
free: () => {},
|
|
125
|
+
serialize: () => new Uint8Array(),
|
|
126
|
+
safe_serialize: () => new Uint8Array(),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Setup fetch mock for http://localhost:3001/verify
|
|
130
|
+
// Simulates verification of zk proof
|
|
131
|
+
// Returns {ctHash: stringified value, signature: `${account_addr}-${security_zone}-${chain_id}-`, recid: 0}
|
|
132
|
+
// Expects the proof to be created by the MockZkListBuilder `build_with_proof_packed` above
|
|
133
|
+
const mockFetch = vi.fn();
|
|
134
|
+
global.fetch = mockFetch;
|
|
135
|
+
const setupZkVerifyMock = () => {
|
|
136
|
+
mockFetch.mockImplementation((url: string, options: any) => {
|
|
137
|
+
if (url === `${MockZkVerifierUrl}/verify`) {
|
|
138
|
+
const body = JSON.parse(options.body as string);
|
|
139
|
+
const { packed_list, account_addr, security_zone, chain_id } = body;
|
|
140
|
+
|
|
141
|
+
// Decode the proof data
|
|
142
|
+
const arr = fromHexString(packed_list);
|
|
143
|
+
const decoded = new TextDecoder().decode(arr);
|
|
144
|
+
const decodedData = parseWithBigInt(decoded);
|
|
145
|
+
const { items } = decodedData;
|
|
146
|
+
|
|
147
|
+
// Create mock verify results
|
|
148
|
+
const mockResults = items.map((item: EncryptableItem) => ({
|
|
149
|
+
ct_hash: BigInt(item.data).toString(),
|
|
150
|
+
signature: `${account_addr}-${security_zone}-${chain_id}-`,
|
|
151
|
+
recid: 0,
|
|
152
|
+
}));
|
|
153
|
+
|
|
154
|
+
return Promise.resolve({
|
|
155
|
+
ok: true,
|
|
156
|
+
json: () =>
|
|
157
|
+
Promise.resolve({
|
|
158
|
+
status: 'success',
|
|
159
|
+
data: mockResults,
|
|
160
|
+
error: null,
|
|
161
|
+
}),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// For other URLs, return a 404
|
|
166
|
+
return Promise.resolve({
|
|
167
|
+
ok: false,
|
|
168
|
+
status: 404,
|
|
169
|
+
text: () => Promise.resolve('Not Found'),
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Create a test keysStorage instance (non-persisted for tests)
|
|
175
|
+
let keysStorage: KeysStorage;
|
|
176
|
+
|
|
177
|
+
const insertMockKeys = (chainId: number, securityZone: number) => {
|
|
178
|
+
keysStorage.setFheKey(chainId, securityZone, '0x1234567890');
|
|
179
|
+
keysStorage.setCrs(chainId, '0x1234567890');
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const mockTfhePublicKeyDeserializer: FheKeyDeserializer = (buff: string) => {
|
|
183
|
+
return buff;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const mockCompactPkeCrsDeserializer: FheKeyDeserializer = (buff: string) => {
|
|
187
|
+
return buff;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const mockInitTfhe: TfheInitializer = () => {
|
|
191
|
+
return Promise.resolve(true);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const mockZkBuilderAndCrsGenerator: ZkBuilderAndCrsGenerator = (fhe: string, crs: string) => {
|
|
195
|
+
return {
|
|
196
|
+
zkBuilder: new MockZkListBuilder(),
|
|
197
|
+
zkCrs: MockCrs,
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const createMockCofhesdkConfig = (chainId: number, zkVerifierUrl: string) => {
|
|
202
|
+
return createCofhesdkConfigBase({
|
|
203
|
+
supportedChains: [
|
|
204
|
+
{
|
|
205
|
+
id: chainId,
|
|
206
|
+
name: 'Mock Chain',
|
|
207
|
+
network: 'Mock Network',
|
|
208
|
+
coFheUrl: MockZkVerifierUrl,
|
|
209
|
+
thresholdNetworkUrl: MockZkVerifierUrl,
|
|
210
|
+
environment: 'TESTNET',
|
|
211
|
+
verifierUrl: zkVerifierUrl,
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
});
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
class MockZkProvenList {
|
|
218
|
+
private items: EncryptableItem[];
|
|
219
|
+
private metadata: Uint8Array;
|
|
220
|
+
|
|
221
|
+
constructor(items: EncryptableItem[], metadata: Uint8Array) {
|
|
222
|
+
this.items = items;
|
|
223
|
+
this.metadata = metadata;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
serialize(): Uint8Array {
|
|
227
|
+
// Serialize this.items into JSON, then encode as Uint8Array (utf-8)
|
|
228
|
+
const json = stringifyWithBigInt({ items: this.items, metadata: this.metadata });
|
|
229
|
+
return new TextEncoder().encode(json);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
describe('EncryptInputsBuilder', () => {
|
|
234
|
+
const defaultSender = '0x1234567890123456789012345678901234567890';
|
|
235
|
+
const defaultChainId = 1;
|
|
236
|
+
const createDefaultParams = () => {
|
|
237
|
+
return {
|
|
238
|
+
inputs: [Encryptable.uint128(100n)] as [EncryptableUint128],
|
|
239
|
+
account: defaultSender,
|
|
240
|
+
chainId: defaultChainId,
|
|
241
|
+
|
|
242
|
+
config: createMockCofhesdkConfig(defaultChainId, MockZkVerifierUrl),
|
|
243
|
+
publicClient: publicClient,
|
|
244
|
+
walletClient: bobWalletClient,
|
|
245
|
+
|
|
246
|
+
tfhePublicKeyDeserializer: mockTfhePublicKeyDeserializer,
|
|
247
|
+
compactPkeCrsDeserializer: mockCompactPkeCrsDeserializer,
|
|
248
|
+
zkBuilderAndCrsGenerator: mockZkBuilderAndCrsGenerator,
|
|
249
|
+
initTfhe: mockInitTfhe,
|
|
250
|
+
zkProveWorkerFn: undefined,
|
|
251
|
+
keysStorage: keysStorage,
|
|
252
|
+
requireConnected: vi.fn(),
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
let builder: EncryptInputsBuilder<[EncryptableUint128]>;
|
|
257
|
+
|
|
258
|
+
beforeEach(() => {
|
|
259
|
+
// Create a fresh keysStorage instance for each test (non-persisted)
|
|
260
|
+
keysStorage = createKeysStore(null);
|
|
261
|
+
setupZkVerifyMock();
|
|
262
|
+
insertMockKeys(defaultChainId, 0);
|
|
263
|
+
builder = new EncryptInputsBuilder(createDefaultParams());
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
describe('constructor and initialization', () => {
|
|
267
|
+
it('should initialize with default values', () => {
|
|
268
|
+
expect(builder).toBeInstanceOf(EncryptInputsBuilder);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should set default security zone to 0', () => {
|
|
272
|
+
const builderWithDefaultZone = new EncryptInputsBuilder({
|
|
273
|
+
...createDefaultParams(),
|
|
274
|
+
securityZone: undefined,
|
|
275
|
+
});
|
|
276
|
+
// We can't directly test private properties, but we can test behavior
|
|
277
|
+
expect(builderWithDefaultZone).toBeInstanceOf(EncryptInputsBuilder);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('should throw an error if config is not set', async () => {
|
|
281
|
+
// Should throw before .encrypt() is called
|
|
282
|
+
try {
|
|
283
|
+
new EncryptInputsBuilder({
|
|
284
|
+
...createDefaultParams(),
|
|
285
|
+
config: undefined as unknown as CofhesdkConfig,
|
|
286
|
+
});
|
|
287
|
+
} catch (error) {
|
|
288
|
+
expect(error).toBeInstanceOf(CofhesdkError);
|
|
289
|
+
expect((error as CofhesdkError).code).toBe(CofhesdkErrorCode.MissingConfig);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should throw an error if tfhePublicKeyDeserializer is not set', async () => {
|
|
294
|
+
// Should throw before .encrypt() is called
|
|
295
|
+
try {
|
|
296
|
+
new EncryptInputsBuilder({
|
|
297
|
+
...createDefaultParams(),
|
|
298
|
+
tfhePublicKeyDeserializer: undefined as unknown as FheKeyDeserializer,
|
|
299
|
+
});
|
|
300
|
+
} catch (error) {
|
|
301
|
+
expect(error).toBeInstanceOf(CofhesdkError);
|
|
302
|
+
expect((error as CofhesdkError).code).toBe(CofhesdkErrorCode.MissingTfhePublicKeyDeserializer);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('should throw an error if compactPkeCrsDeserializer is not set', async () => {
|
|
307
|
+
// Should throw before .encrypt() is called
|
|
308
|
+
try {
|
|
309
|
+
new EncryptInputsBuilder({
|
|
310
|
+
...createDefaultParams(),
|
|
311
|
+
compactPkeCrsDeserializer: undefined as unknown as FheKeyDeserializer,
|
|
312
|
+
});
|
|
313
|
+
} catch (error) {
|
|
314
|
+
expect(error).toBeInstanceOf(CofhesdkError);
|
|
315
|
+
expect((error as CofhesdkError).code).toBe(CofhesdkErrorCode.MissingCompactPkeCrsDeserializer);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('should throw an error if initTfhe throws an error', async () => {
|
|
320
|
+
try {
|
|
321
|
+
await new EncryptInputsBuilder({
|
|
322
|
+
...createDefaultParams(),
|
|
323
|
+
initTfhe: vi.fn().mockRejectedValue(new Error('Failed to initialize TFHE')),
|
|
324
|
+
}).encrypt();
|
|
325
|
+
} catch (error) {
|
|
326
|
+
expect(error).toBeInstanceOf(CofhesdkError);
|
|
327
|
+
expect((error as CofhesdkError).code).toBe(CofhesdkErrorCode.InitTfheFailed);
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should not throw an error if initTfhe is set', async () => {
|
|
332
|
+
const result = await new EncryptInputsBuilder({
|
|
333
|
+
...createDefaultParams(),
|
|
334
|
+
initTfhe: mockInitTfhe,
|
|
335
|
+
}).encrypt();
|
|
336
|
+
expect(result).toBeDefined();
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
describe('sender', () => {
|
|
341
|
+
it('should set sender and return builder for chaining', () => {
|
|
342
|
+
const sender = '0x9876543210987654321098765432109876543210';
|
|
343
|
+
|
|
344
|
+
const result = builder.setAccount(sender);
|
|
345
|
+
|
|
346
|
+
expect(result).toBe(builder);
|
|
347
|
+
expect(result.getAccount()).toBe(sender);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('should allow chaining with other methods', () => {
|
|
351
|
+
const sender = '0x1111111111111111111111111111111111111111';
|
|
352
|
+
const securityZone = 5;
|
|
353
|
+
|
|
354
|
+
const result = builder
|
|
355
|
+
.setAccount(sender)
|
|
356
|
+
.setSecurityZone(securityZone)
|
|
357
|
+
.setStepCallback(() => {});
|
|
358
|
+
|
|
359
|
+
expect(result).toBe(builder);
|
|
360
|
+
expect(result.getAccount()).toBe(sender);
|
|
361
|
+
expect(result.getSecurityZone()).toBe(securityZone);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('should throw an error if account is not set', async () => {
|
|
365
|
+
try {
|
|
366
|
+
await new EncryptInputsBuilder({
|
|
367
|
+
...createDefaultParams(),
|
|
368
|
+
account: undefined,
|
|
369
|
+
}).encrypt();
|
|
370
|
+
} catch (error) {
|
|
371
|
+
expect(error).toBeInstanceOf(CofhesdkError);
|
|
372
|
+
expect((error as CofhesdkError).code).toBe(CofhesdkErrorCode.AccountUninitialized);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
describe('setSecurityZone', () => {
|
|
378
|
+
it('should set security zone and return builder for chaining', () => {
|
|
379
|
+
const securityZone = 42;
|
|
380
|
+
const result = builder.setSecurityZone(securityZone);
|
|
381
|
+
expect(result).toBe(builder);
|
|
382
|
+
expect(result.getSecurityZone()).toBe(securityZone);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should allow chaining with other methods', () => {
|
|
386
|
+
const sender = '0x2222222222222222222222222222222222222222';
|
|
387
|
+
const securityZone = 10;
|
|
388
|
+
|
|
389
|
+
const result = builder
|
|
390
|
+
.setSecurityZone(securityZone)
|
|
391
|
+
.setAccount(sender)
|
|
392
|
+
.setStepCallback(() => {});
|
|
393
|
+
|
|
394
|
+
expect(result).toBe(builder);
|
|
395
|
+
expect(result.getAccount()).toBe(sender);
|
|
396
|
+
expect(result.getSecurityZone()).toBe(securityZone);
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
describe('chainId', () => {
|
|
401
|
+
it('should set chain id and return builder for chaining', () => {
|
|
402
|
+
const chainId = 2;
|
|
403
|
+
const result = builder.setChainId(chainId);
|
|
404
|
+
expect(result).toBe(builder);
|
|
405
|
+
expect(result.getChainId()).toBe(chainId);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it('should throw an error if chainId is not set', async () => {
|
|
409
|
+
try {
|
|
410
|
+
await new EncryptInputsBuilder({
|
|
411
|
+
...createDefaultParams(),
|
|
412
|
+
chainId: undefined,
|
|
413
|
+
}).encrypt();
|
|
414
|
+
} catch (error) {
|
|
415
|
+
expect(error).toBeInstanceOf(CofhesdkError);
|
|
416
|
+
expect((error as CofhesdkError).code).toBe(CofhesdkErrorCode.ChainIdUninitialized);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
describe('zkVerifierUrl', () => {
|
|
422
|
+
it('should throw if zkVerifierUrl is not set', async () => {
|
|
423
|
+
try {
|
|
424
|
+
await new EncryptInputsBuilder({
|
|
425
|
+
...createDefaultParams(),
|
|
426
|
+
inputs: [Encryptable.uint128(100n)] as [EncryptableUint128],
|
|
427
|
+
account: '0x1234567890123456789012345678901234567890',
|
|
428
|
+
chainId: 1,
|
|
429
|
+
config: createMockCofhesdkConfig(defaultChainId, undefined as unknown as string),
|
|
430
|
+
}).encrypt();
|
|
431
|
+
} catch (error) {
|
|
432
|
+
expect(error).toBeInstanceOf(CofhesdkError);
|
|
433
|
+
expect((error as CofhesdkError).code).toBe(CofhesdkErrorCode.ZkVerifierUrlUninitialized);
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
describe('setStepCallback', () => {
|
|
439
|
+
it('should set step callback and return builder for chaining', () => {
|
|
440
|
+
const callback = vi.fn();
|
|
441
|
+
const result = builder.setStepCallback(callback);
|
|
442
|
+
expect(result).toBe(builder);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('should allow chaining with other methods', () => {
|
|
446
|
+
const callback = vi.fn();
|
|
447
|
+
const result = builder.setStepCallback(callback).setSecurityZone(15);
|
|
448
|
+
|
|
449
|
+
expect(result).toBe(builder);
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
describe('encrypt', () => {
|
|
454
|
+
it('should execute the full encryption flow with step callbacks', async () => {
|
|
455
|
+
const stepCallback = vi.fn();
|
|
456
|
+
builder.setStepCallback(stepCallback);
|
|
457
|
+
|
|
458
|
+
const result = await builder.encrypt();
|
|
459
|
+
|
|
460
|
+
// Verify step callbacks were called in order
|
|
461
|
+
expect(stepCallback).toHaveBeenCalledTimes(10);
|
|
462
|
+
|
|
463
|
+
expect(stepCallback).toHaveBeenNthCalledWith(
|
|
464
|
+
1,
|
|
465
|
+
EncryptStep.InitTfhe,
|
|
466
|
+
expect.objectContaining({
|
|
467
|
+
isStart: true,
|
|
468
|
+
isEnd: false,
|
|
469
|
+
duration: 0,
|
|
470
|
+
})
|
|
471
|
+
);
|
|
472
|
+
expect(stepCallback).toHaveBeenNthCalledWith(
|
|
473
|
+
2,
|
|
474
|
+
EncryptStep.InitTfhe,
|
|
475
|
+
expect.objectContaining({
|
|
476
|
+
isStart: false,
|
|
477
|
+
isEnd: true,
|
|
478
|
+
duration: expect.any(Number),
|
|
479
|
+
})
|
|
480
|
+
);
|
|
481
|
+
expect(stepCallback).toHaveBeenNthCalledWith(
|
|
482
|
+
3,
|
|
483
|
+
EncryptStep.FetchKeys,
|
|
484
|
+
expect.objectContaining({
|
|
485
|
+
isStart: true,
|
|
486
|
+
isEnd: false,
|
|
487
|
+
duration: 0,
|
|
488
|
+
})
|
|
489
|
+
);
|
|
490
|
+
expect(stepCallback).toHaveBeenNthCalledWith(
|
|
491
|
+
4,
|
|
492
|
+
EncryptStep.FetchKeys,
|
|
493
|
+
expect.objectContaining({
|
|
494
|
+
isStart: false,
|
|
495
|
+
isEnd: true,
|
|
496
|
+
duration: expect.any(Number),
|
|
497
|
+
})
|
|
498
|
+
);
|
|
499
|
+
expect(stepCallback).toHaveBeenNthCalledWith(
|
|
500
|
+
5,
|
|
501
|
+
EncryptStep.Pack,
|
|
502
|
+
expect.objectContaining({
|
|
503
|
+
isStart: true,
|
|
504
|
+
isEnd: false,
|
|
505
|
+
duration: 0,
|
|
506
|
+
})
|
|
507
|
+
);
|
|
508
|
+
expect(stepCallback).toHaveBeenNthCalledWith(
|
|
509
|
+
6,
|
|
510
|
+
EncryptStep.Pack,
|
|
511
|
+
expect.objectContaining({
|
|
512
|
+
isStart: false,
|
|
513
|
+
isEnd: true,
|
|
514
|
+
duration: expect.any(Number),
|
|
515
|
+
})
|
|
516
|
+
);
|
|
517
|
+
expect(stepCallback).toHaveBeenNthCalledWith(
|
|
518
|
+
7,
|
|
519
|
+
EncryptStep.Prove,
|
|
520
|
+
expect.objectContaining({
|
|
521
|
+
isStart: true,
|
|
522
|
+
isEnd: false,
|
|
523
|
+
duration: 0,
|
|
524
|
+
})
|
|
525
|
+
);
|
|
526
|
+
expect(stepCallback).toHaveBeenNthCalledWith(
|
|
527
|
+
8,
|
|
528
|
+
EncryptStep.Prove,
|
|
529
|
+
expect.objectContaining({
|
|
530
|
+
isStart: false,
|
|
531
|
+
isEnd: true,
|
|
532
|
+
duration: expect.any(Number),
|
|
533
|
+
})
|
|
534
|
+
);
|
|
535
|
+
expect(stepCallback).toHaveBeenNthCalledWith(
|
|
536
|
+
9,
|
|
537
|
+
EncryptStep.Verify,
|
|
538
|
+
expect.objectContaining({
|
|
539
|
+
isStart: true,
|
|
540
|
+
isEnd: false,
|
|
541
|
+
duration: 0,
|
|
542
|
+
})
|
|
543
|
+
);
|
|
544
|
+
expect(stepCallback).toHaveBeenNthCalledWith(
|
|
545
|
+
10,
|
|
546
|
+
EncryptStep.Verify,
|
|
547
|
+
expect.objectContaining({
|
|
548
|
+
isStart: false,
|
|
549
|
+
isEnd: true,
|
|
550
|
+
duration: expect.any(Number),
|
|
551
|
+
})
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
// Verify result structure
|
|
555
|
+
expect(result).toBeDefined();
|
|
556
|
+
expect(Array.isArray(result)).toBe(true);
|
|
557
|
+
|
|
558
|
+
// Verify result embedded metadata
|
|
559
|
+
const [encrypted] = result;
|
|
560
|
+
const encryptedMetadata = unpackMetadata(encrypted.signature);
|
|
561
|
+
expect(encryptedMetadata).toBeDefined();
|
|
562
|
+
expect(encryptedMetadata.signer).toBe(defaultSender);
|
|
563
|
+
expect(encryptedMetadata.securityZone).toBe(0);
|
|
564
|
+
expect(encryptedMetadata.chainId).toBe(defaultChainId);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
it('should use overridden account when set', async () => {
|
|
568
|
+
const overriddenSender = '0x5555555555555555555555555555555555555555';
|
|
569
|
+
builder.setAccount(overriddenSender);
|
|
570
|
+
|
|
571
|
+
const result = await builder.encrypt();
|
|
572
|
+
|
|
573
|
+
// Verify result embedded metadata
|
|
574
|
+
const [encrypted] = result;
|
|
575
|
+
const encryptedMetadata = unpackMetadata(encrypted.signature);
|
|
576
|
+
expect(encryptedMetadata).toBeDefined();
|
|
577
|
+
expect(encryptedMetadata.signer).toBe(overriddenSender);
|
|
578
|
+
expect(encryptedMetadata.securityZone).toBe(0);
|
|
579
|
+
expect(encryptedMetadata.chainId).toBe(defaultChainId);
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it('should use overridden security zone when set', async () => {
|
|
583
|
+
const overriddenZone = 7;
|
|
584
|
+
builder.setSecurityZone(overriddenZone);
|
|
585
|
+
|
|
586
|
+
insertMockKeys(defaultChainId, overriddenZone);
|
|
587
|
+
|
|
588
|
+
const result = await builder.encrypt();
|
|
589
|
+
|
|
590
|
+
// Verify result embedded metadata
|
|
591
|
+
const [encrypted] = result;
|
|
592
|
+
const encryptedMetadata = unpackMetadata(encrypted.signature);
|
|
593
|
+
expect(encryptedMetadata).toBeDefined();
|
|
594
|
+
expect(encryptedMetadata.signer).toBe(defaultSender);
|
|
595
|
+
expect(encryptedMetadata.securityZone).toBe(overriddenZone);
|
|
596
|
+
expect(encryptedMetadata.chainId).toBe(defaultChainId);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it('should work without step callback', async () => {
|
|
600
|
+
// No step callback set
|
|
601
|
+
const result = await builder.encrypt();
|
|
602
|
+
|
|
603
|
+
expect(result).toBeDefined();
|
|
604
|
+
expect(Array.isArray(result)).toBe(true);
|
|
605
|
+
// Should not throw when no callback is set
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
it('should handle multiple input types', async () => {
|
|
609
|
+
const multiInputBuilder = new EncryptInputsBuilder({
|
|
610
|
+
...createDefaultParams(),
|
|
611
|
+
inputs: [Encryptable.uint128(100n), Encryptable.bool(true)] as [
|
|
612
|
+
ReturnType<typeof Encryptable.uint128>,
|
|
613
|
+
ReturnType<typeof Encryptable.bool>,
|
|
614
|
+
],
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
const result = await multiInputBuilder.encrypt();
|
|
618
|
+
|
|
619
|
+
expect(result).toBeDefined();
|
|
620
|
+
expect(Array.isArray(result)).toBe(true);
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it('should throw an error if total bits exceeds 2048', async () => {
|
|
624
|
+
try {
|
|
625
|
+
await new EncryptInputsBuilder({
|
|
626
|
+
...createDefaultParams(),
|
|
627
|
+
inputs: [
|
|
628
|
+
Encryptable.uint128(100n),
|
|
629
|
+
Encryptable.uint128(100n),
|
|
630
|
+
Encryptable.uint128(100n),
|
|
631
|
+
Encryptable.uint128(100n),
|
|
632
|
+
Encryptable.uint128(100n),
|
|
633
|
+
Encryptable.uint128(100n),
|
|
634
|
+
Encryptable.uint128(100n),
|
|
635
|
+
Encryptable.uint128(100n),
|
|
636
|
+
Encryptable.uint128(100n),
|
|
637
|
+
Encryptable.uint128(100n),
|
|
638
|
+
Encryptable.uint128(100n),
|
|
639
|
+
Encryptable.uint128(100n),
|
|
640
|
+
Encryptable.uint128(100n),
|
|
641
|
+
Encryptable.uint128(100n),
|
|
642
|
+
Encryptable.uint128(100n),
|
|
643
|
+
Encryptable.uint128(100n),
|
|
644
|
+
Encryptable.uint128(100n),
|
|
645
|
+
Encryptable.uint128(100n),
|
|
646
|
+
Encryptable.uint128(100n),
|
|
647
|
+
Encryptable.uint128(100n),
|
|
648
|
+
],
|
|
649
|
+
}).encrypt();
|
|
650
|
+
} catch (error) {
|
|
651
|
+
expect(error).toBeInstanceOf(CofhesdkError);
|
|
652
|
+
expect((error as CofhesdkError).code).toBe(CofhesdkErrorCode.ZkPackFailed);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
it('should throw an error if utype is invalid', async () => {
|
|
657
|
+
try {
|
|
658
|
+
await new EncryptInputsBuilder({
|
|
659
|
+
...createDefaultParams(),
|
|
660
|
+
inputs: [
|
|
661
|
+
{
|
|
662
|
+
data: 10n,
|
|
663
|
+
utype: FheTypes.Uint10, // Invalid utype
|
|
664
|
+
},
|
|
665
|
+
] as unknown as [EncryptableItem],
|
|
666
|
+
});
|
|
667
|
+
} catch (error) {
|
|
668
|
+
expect(error).toBeInstanceOf(CofhesdkError);
|
|
669
|
+
expect((error as CofhesdkError).code).toBe(CofhesdkErrorCode.ZkPackFailed);
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
// TODO: Implement error handling tests
|
|
675
|
+
// describe('error handling', () => {
|
|
676
|
+
// it('should handle ZK pack errors gracefully', async () => {
|
|
677
|
+
// const result = await builder.encrypt();
|
|
678
|
+
// expectResultError(result, CofhesdkErrorCode.InternalError, 'ZK pack failed');
|
|
679
|
+
// });
|
|
680
|
+
|
|
681
|
+
// it('should handle ZK prove errors gracefully', async () => {
|
|
682
|
+
// const result = await builder.encrypt();
|
|
683
|
+
// expectResultError(result, CofhesdkErrorCode.InternalError, 'ZK prove failed');
|
|
684
|
+
// });
|
|
685
|
+
|
|
686
|
+
// it('should handle ZK verify errors gracefully', async () => {
|
|
687
|
+
// const result = await builder.encrypt();
|
|
688
|
+
// expectResultError(result, CofhesdkErrorCode.InternalError, 'ZK verify failed');
|
|
689
|
+
// });
|
|
690
|
+
// });
|
|
691
|
+
|
|
692
|
+
describe('integration scenarios', () => {
|
|
693
|
+
it('should work with the complete builder chain', async () => {
|
|
694
|
+
const sender = '0x9999999999999999999999999999999999999999';
|
|
695
|
+
const securityZone = 3;
|
|
696
|
+
|
|
697
|
+
insertMockKeys(defaultChainId, securityZone);
|
|
698
|
+
|
|
699
|
+
const stepCallback = vi.fn();
|
|
700
|
+
const result = await builder
|
|
701
|
+
.setAccount(sender)
|
|
702
|
+
.setSecurityZone(securityZone)
|
|
703
|
+
.setStepCallback(stepCallback)
|
|
704
|
+
.encrypt();
|
|
705
|
+
|
|
706
|
+
expect(result).toBeDefined();
|
|
707
|
+
expect(stepCallback).toHaveBeenCalledTimes(10);
|
|
708
|
+
|
|
709
|
+
// Verify result embedded metadata
|
|
710
|
+
const [encrypted] = result;
|
|
711
|
+
const encryptedMetadata = unpackMetadata(encrypted.signature);
|
|
712
|
+
expect(encryptedMetadata).toBeDefined();
|
|
713
|
+
expect(encryptedMetadata.signer).toBe(sender);
|
|
714
|
+
expect(encryptedMetadata.securityZone).toBe(securityZone);
|
|
715
|
+
expect(encryptedMetadata.chainId).toBe(defaultChainId);
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
it('should maintain state across method calls', async () => {
|
|
719
|
+
const sender = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
|
|
720
|
+
const securityZone = 99;
|
|
721
|
+
|
|
722
|
+
insertMockKeys(defaultChainId, securityZone);
|
|
723
|
+
|
|
724
|
+
builder.setAccount(sender);
|
|
725
|
+
builder.setSecurityZone(securityZone);
|
|
726
|
+
|
|
727
|
+
// Call encrypt multiple times to ensure state is maintained
|
|
728
|
+
const result1 = await builder.encrypt();
|
|
729
|
+
const result2 = await builder.encrypt();
|
|
730
|
+
|
|
731
|
+
expect(result1).toBeDefined();
|
|
732
|
+
expect(result2).toBeDefined();
|
|
733
|
+
|
|
734
|
+
// Verify result embedded metadata
|
|
735
|
+
const [encrypted1] = result1;
|
|
736
|
+
const encryptedMetadata1 = unpackMetadata(encrypted1.signature);
|
|
737
|
+
expect(encryptedMetadata1).toBeDefined();
|
|
738
|
+
expect(encryptedMetadata1.signer).toBe(sender);
|
|
739
|
+
expect(encryptedMetadata1.securityZone).toBe(securityZone);
|
|
740
|
+
expect(encryptedMetadata1.chainId).toBe(defaultChainId);
|
|
741
|
+
|
|
742
|
+
// Verify result embedded metadata
|
|
743
|
+
const [encrypted2] = result2;
|
|
744
|
+
const encryptedMetadata2 = unpackMetadata(encrypted2.signature);
|
|
745
|
+
expect(encryptedMetadata2).toBeDefined();
|
|
746
|
+
expect(encryptedMetadata2.signer).toBe(sender);
|
|
747
|
+
expect(encryptedMetadata2.securityZone).toBe(securityZone);
|
|
748
|
+
expect(encryptedMetadata2.chainId).toBe(defaultChainId);
|
|
749
|
+
});
|
|
750
|
+
});
|
|
751
|
+
});
|