@cofhe/sdk 0.4.0 → 0.5.1
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 +38 -0
- package/adapters/{ethers5.test.ts → test/ethers5.test.ts} +2 -2
- package/adapters/{ethers6.test.ts → test/ethers6.test.ts} +2 -2
- package/adapters/{hardhat.hh2.test.ts → test/hardhat.hh2.test.ts} +2 -2
- package/adapters/{index.test.ts → test/index.test.ts} +1 -1
- package/adapters/{wagmi.test.ts → test/wagmi.test.ts} +1 -1
- package/chains/{chains.test.ts → test/chains.test.ts} +1 -1
- package/core/client.ts +11 -1
- package/core/clientTypes.ts +3 -1
- package/core/consts.ts +9 -0
- package/core/decrypt/cofheMocksDecryptForTx.ts +14 -3
- package/core/decrypt/decryptForTxBuilder.ts +16 -2
- package/core/decrypt/decryptForViewBuilder.ts +14 -7
- package/core/decrypt/polling.ts +14 -0
- package/core/decrypt/tnDecryptV2.ts +250 -110
- package/core/decrypt/tnSealOutputV2.ts +245 -104
- package/core/decrypt/verifyDecryptResult.ts +65 -0
- package/core/encrypt/cofheMocksZkVerifySign.ts +6 -6
- package/core/encrypt/zkPackProveVerify.ts +10 -19
- package/core/fetchKeys.ts +0 -2
- package/core/index.ts +9 -1
- package/core/keyStore.ts +5 -2
- package/core/permits.ts +5 -0
- package/core/{client.test.ts → test/client.test.ts} +7 -7
- package/core/{config.test.ts → test/config.test.ts} +1 -1
- package/core/test/decrypt.test.ts +252 -0
- package/core/test/decryptBuilders.test.ts +390 -0
- package/core/{encrypt → test}/encryptInputsBuilder.test.ts +61 -6
- package/core/{fetchKeys.test.ts → test/fetchKeys.test.ts} +3 -3
- package/core/{keyStore.test.ts → test/keyStore.test.ts} +5 -3
- package/core/{permits.test.ts → test/permits.test.ts} +42 -1
- package/core/test/pollCallbacks.test.ts +563 -0
- package/core/types.ts +13 -0
- package/dist/chains.d.cts +2 -2
- package/dist/chains.d.ts +2 -2
- package/dist/chunk-4FP4V35O.js +13 -0
- package/dist/{chunk-NWDKXBIP.js → chunk-MRCKUMOS.js} +62 -22
- package/dist/{chunk-MXND5SVN.js → chunk-S7OKGLFD.js} +485 -207
- package/dist/{clientTypes-kkrRdawm.d.ts → clientTypes-BSbwairE.d.cts} +23 -6
- package/dist/{clientTypes-ACVWbrXL.d.cts → clientTypes-DDmcgZ0a.d.ts} +23 -6
- package/dist/core.cjs +561 -244
- package/dist/core.d.cts +24 -6
- package/dist/core.d.ts +24 -6
- package/dist/core.js +3 -2
- package/dist/node.cjs +566 -246
- package/dist/node.d.cts +3 -3
- package/dist/node.d.ts +3 -3
- package/dist/node.js +14 -7
- package/dist/{permit-MZ502UBl.d.cts → permit-DnVMDT5h.d.cts} +34 -4
- package/dist/{permit-MZ502UBl.d.ts → permit-DnVMDT5h.d.ts} +34 -4
- package/dist/permits.cjs +66 -29
- package/dist/permits.d.cts +18 -13
- package/dist/permits.d.ts +18 -13
- package/dist/permits.js +2 -1
- package/dist/web.cjs +604 -256
- package/dist/web.d.cts +8 -4
- package/dist/web.d.ts +8 -4
- package/dist/web.js +49 -14
- package/dist/zkProve.worker.cjs +72 -64
- package/dist/zkProve.worker.js +71 -64
- package/node/index.ts +13 -4
- package/node/test/client.test.ts +25 -0
- package/node/test/config.test.ts +16 -0
- package/node/test/inherited.test.ts +244 -0
- package/node/test/tfheinit.test.ts +56 -0
- package/package.json +24 -22
- package/permits/permit.ts +31 -5
- package/permits/sealing.ts +1 -1
- package/permits/{localstorage.test.ts → test/localstorage.test.ts} +2 -2
- package/permits/{permit.test.ts → test/permit.test.ts} +35 -1
- package/permits/{sealing.test.ts → test/sealing.test.ts} +1 -1
- package/permits/{store.test.ts → test/store.test.ts} +2 -2
- package/permits/{validation.test.ts → test/validation.test.ts} +82 -6
- package/permits/types.ts +1 -1
- package/permits/validation.ts +42 -2
- package/web/const.ts +2 -0
- package/web/index.ts +40 -11
- package/web/storage.ts +18 -3
- package/web/{client.web.test.ts → test/client.web.test.ts} +13 -1
- package/web/test/config.web.test.ts +16 -0
- package/web/test/inherited.web.test.ts +245 -0
- package/web/test/tfheinit.web.test.ts +62 -0
- package/web/{worker.config.web.test.ts → test/worker.config.web.test.ts} +1 -1
- package/web/{worker.output.web.test.ts → test/worker.output.web.test.ts} +1 -1
- package/web/{workerManager.test.ts → test/workerManager.test.ts} +1 -1
- package/web/{workerManager.web.test.ts → test/workerManager.web.test.ts} +1 -1
- package/web/zkProve.worker.ts +94 -84
- package/node/client.test.ts +0 -147
- package/node/config.test.ts +0 -68
- package/node/encryptInputs.test.ts +0 -155
- package/web/config.web.test.ts +0 -69
- package/web/encryptInputs.web.test.ts +0 -172
- package/web/worker.builder.web.test.ts +0 -148
- /package/dist/{types-YiAC4gig.d.cts → types-C07FK-cL.d.cts} +0 -0
- /package/dist/{types-YiAC4gig.d.ts → types-C07FK-cL.d.ts} +0 -0
package/core/permits.ts
CHANGED
|
@@ -75,6 +75,10 @@ const getHash = (permit: PermitHashFields) => {
|
|
|
75
75
|
return PermitUtils.getHash(permit);
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
+
const exportShared = (permit: Permit) => {
|
|
79
|
+
return PermitUtils.export(permit);
|
|
80
|
+
};
|
|
81
|
+
|
|
78
82
|
const serialize = (permit: Permit) => {
|
|
79
83
|
return PermitUtils.serialize(permit);
|
|
80
84
|
};
|
|
@@ -188,6 +192,7 @@ export const permits = {
|
|
|
188
192
|
getOrCreateSharingPermit,
|
|
189
193
|
|
|
190
194
|
getHash,
|
|
195
|
+
export: exportShared,
|
|
191
196
|
serialize,
|
|
192
197
|
deserialize,
|
|
193
198
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { createCofheClientBase } from '
|
|
3
|
-
import { type CofheClient, type CofheClientConnectionState } from '
|
|
4
|
-
import { createCofheConfigBase, type CofheEnvironment } from '
|
|
5
|
-
import { CofheError, CofheErrorCode } from '
|
|
2
|
+
import { createCofheClientBase } from '../client.js';
|
|
3
|
+
import { type CofheClient, type CofheClientConnectionState } from '../clientTypes.js';
|
|
4
|
+
import { createCofheConfigBase, type CofheEnvironment } from '../config.js';
|
|
5
|
+
import { CofheError, CofheErrorCode } from '../error.js';
|
|
6
6
|
import { type PublicClient, type WalletClient } from 'viem';
|
|
7
|
-
import { EncryptInputsBuilder } from '
|
|
8
|
-
import { Encryptable } from '
|
|
7
|
+
import { EncryptInputsBuilder } from '../encrypt/encryptInputsBuilder.js';
|
|
8
|
+
import { Encryptable } from '../types.js';
|
|
9
9
|
|
|
10
10
|
// Mock dependencies
|
|
11
|
-
vi.mock('
|
|
11
|
+
vi.mock('../keyStore', () => ({
|
|
12
12
|
createKeysStore: vi.fn(() => ({
|
|
13
13
|
rehydrateKeysStore: vi.fn().mockResolvedValue(undefined),
|
|
14
14
|
getFheKey: vi.fn(),
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { FheTypes, verifyDecryptResult, createCofheConfigBase, TASK_MANAGER_ADDRESS } from '@/core';
|
|
2
|
+
import { getChainById } from '@/chains';
|
|
3
|
+
import {
|
|
4
|
+
TEST_PRIVATE_KEY,
|
|
5
|
+
PRIMARY_TEST_CHAIN,
|
|
6
|
+
primaryTestChainRegistry,
|
|
7
|
+
isPrimaryTestChainReady,
|
|
8
|
+
} from '@cofhe/test-setup';
|
|
9
|
+
|
|
10
|
+
import { permits } from '../permits.js';
|
|
11
|
+
import { DecryptForTxBuilder } from '../decrypt/decryptForTxBuilder.js';
|
|
12
|
+
import { DecryptForViewBuilder } from '../decrypt/decryptForViewBuilder.js';
|
|
13
|
+
|
|
14
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
15
|
+
import type { Chain, PublicClient, WalletClient } from 'viem';
|
|
16
|
+
import { createPublicClient, createWalletClient, http, parseAbi } from 'viem';
|
|
17
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
18
|
+
import { arbitrumSepolia, baseSepolia, sepolia } from 'viem/chains';
|
|
19
|
+
|
|
20
|
+
const account = privateKeyToAccount(TEST_PRIVATE_KEY);
|
|
21
|
+
|
|
22
|
+
const VIEM_CHAINS: Record<number, Chain> = {
|
|
23
|
+
421614: arbitrumSepolia,
|
|
24
|
+
84532: baseSepolia,
|
|
25
|
+
11155111: sepolia,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
describe('Core – Decrypt Tests', () => {
|
|
29
|
+
let publicClient: PublicClient;
|
|
30
|
+
let walletClient: WalletClient;
|
|
31
|
+
let config: ReturnType<typeof createCofheConfigBase>;
|
|
32
|
+
|
|
33
|
+
let privateCtHash: `0x${string}`;
|
|
34
|
+
let privateValue: bigint;
|
|
35
|
+
|
|
36
|
+
let publicCtHash: `0x${string}`;
|
|
37
|
+
let publicValue: bigint;
|
|
38
|
+
|
|
39
|
+
beforeAll(() => {
|
|
40
|
+
if (!isPrimaryTestChainReady(primaryTestChainRegistry)) {
|
|
41
|
+
throw new Error('Primary test chain registry is not initialized. Run `pnpm test:setup` first.');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const reg = primaryTestChainRegistry;
|
|
45
|
+
const chainId = reg.chainId;
|
|
46
|
+
|
|
47
|
+
const viemChain = VIEM_CHAINS[chainId];
|
|
48
|
+
if (!viemChain) throw new Error(`No viem chain mapping for chain ${chainId}`);
|
|
49
|
+
|
|
50
|
+
const cofheChain = getChainById(chainId);
|
|
51
|
+
if (!cofheChain) throw new Error(`No cofhe chain config for chain ${chainId}`);
|
|
52
|
+
|
|
53
|
+
config = createCofheConfigBase({ supportedChains: [cofheChain] });
|
|
54
|
+
|
|
55
|
+
privateCtHash = reg.privateValue.ctHash as `0x${string}`;
|
|
56
|
+
privateValue = BigInt(reg.privateValue.value);
|
|
57
|
+
|
|
58
|
+
publicCtHash = reg.publicValue.ctHash as `0x${string}`;
|
|
59
|
+
publicValue = BigInt(reg.publicValue.value);
|
|
60
|
+
|
|
61
|
+
publicClient = createPublicClient({ chain: viemChain, transport: http() });
|
|
62
|
+
walletClient = createWalletClient({ chain: viemChain, transport: http(), account });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
function txBuilder(ctHash: `0x${string}`) {
|
|
66
|
+
return new DecryptForTxBuilder({
|
|
67
|
+
config,
|
|
68
|
+
publicClient,
|
|
69
|
+
walletClient,
|
|
70
|
+
chainId: PRIMARY_TEST_CHAIN,
|
|
71
|
+
account: account.address,
|
|
72
|
+
ctHash,
|
|
73
|
+
requireConnected: undefined,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function viewBuilder(ctHash: `0x${string}`, utype: FheTypes) {
|
|
78
|
+
return new DecryptForViewBuilder({
|
|
79
|
+
config,
|
|
80
|
+
publicClient,
|
|
81
|
+
walletClient,
|
|
82
|
+
chainId: PRIMARY_TEST_CHAIN,
|
|
83
|
+
account: account.address,
|
|
84
|
+
ctHash,
|
|
85
|
+
utype,
|
|
86
|
+
requireConnected: undefined,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function createPermit() {
|
|
91
|
+
return permits.createSelf({ issuer: account.address, name: 'Decrypt Test Permit' }, publicClient, walletClient);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// decryptForTx – withoutPermit (global allowance)
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
describe('decryptForTx – withoutPermit (global allowance)', () => {
|
|
99
|
+
it('should decrypt a publicly allowed ciphertext', async () => {
|
|
100
|
+
const result = await txBuilder(publicCtHash).withoutPermit().execute();
|
|
101
|
+
|
|
102
|
+
expect(BigInt(result.ctHash)).toBe(BigInt(publicCtHash));
|
|
103
|
+
expect(result.decryptedValue).toBe(publicValue);
|
|
104
|
+
expect(result.signature).toMatch(/^0x[0-9a-fA-F]+$/);
|
|
105
|
+
}, 180000);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// decryptForTx – withPermit
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
describe('decryptForTx – withPermit', () => {
|
|
113
|
+
it('should decrypt with a self permit', async () => {
|
|
114
|
+
const permit = await createPermit();
|
|
115
|
+
|
|
116
|
+
const result = await txBuilder(privateCtHash).withPermit(permit).execute();
|
|
117
|
+
|
|
118
|
+
expect(BigInt(result.ctHash)).toBe(BigInt(privateCtHash));
|
|
119
|
+
expect(result.decryptedValue).toBe(privateValue);
|
|
120
|
+
expect(result.signature).toBeDefined();
|
|
121
|
+
}, 180000);
|
|
122
|
+
|
|
123
|
+
it('should auto-resolve active permit', async () => {
|
|
124
|
+
const permit = await createPermit();
|
|
125
|
+
permits.selectActivePermit(PRIMARY_TEST_CHAIN, account.address, permit.hash);
|
|
126
|
+
|
|
127
|
+
const result = await txBuilder(privateCtHash).withPermit().execute();
|
|
128
|
+
|
|
129
|
+
expect(BigInt(result.ctHash)).toBe(BigInt(privateCtHash));
|
|
130
|
+
expect(result.decryptedValue).toBe(privateValue);
|
|
131
|
+
}, 180000);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// verifyDecryptResult
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
|
|
138
|
+
describe('verifyDecryptResult', () => {
|
|
139
|
+
it('should verify a valid decrypt result', async () => {
|
|
140
|
+
const permit = await createPermit();
|
|
141
|
+
|
|
142
|
+
const decryptResult = await txBuilder(privateCtHash).withPermit(permit).execute();
|
|
143
|
+
|
|
144
|
+
const isValid = await verifyDecryptResult(
|
|
145
|
+
decryptResult.ctHash,
|
|
146
|
+
privateValue,
|
|
147
|
+
decryptResult.signature,
|
|
148
|
+
publicClient
|
|
149
|
+
);
|
|
150
|
+
expect(isValid).toBe(true);
|
|
151
|
+
}, 180000);
|
|
152
|
+
|
|
153
|
+
it('should return false for invalid inputs', async () => {
|
|
154
|
+
const permit = await createPermit();
|
|
155
|
+
|
|
156
|
+
const decryptResult = await txBuilder(privateCtHash).withPermit(permit).execute();
|
|
157
|
+
|
|
158
|
+
expect(
|
|
159
|
+
await verifyDecryptResult(decryptResult.ctHash, privateValue + 1n, decryptResult.signature, publicClient)
|
|
160
|
+
).toBe(false);
|
|
161
|
+
}, 180000);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// decryptForView
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
|
|
168
|
+
describe('decryptForView', () => {
|
|
169
|
+
it('should return the plaintext value', async () => {
|
|
170
|
+
await createPermit();
|
|
171
|
+
|
|
172
|
+
const result = await viewBuilder(privateCtHash, FheTypes.Uint32).execute();
|
|
173
|
+
expect(result).toBe(privateValue);
|
|
174
|
+
}, 180000);
|
|
175
|
+
|
|
176
|
+
it('should agree with decryptForTx on the same handle', async () => {
|
|
177
|
+
const permit = await createPermit();
|
|
178
|
+
|
|
179
|
+
const viewResult = await viewBuilder(privateCtHash, FheTypes.Uint32).execute();
|
|
180
|
+
const txResult = await txBuilder(privateCtHash).withPermit(permit).execute();
|
|
181
|
+
|
|
182
|
+
expect(viewResult).toBe(privateValue);
|
|
183
|
+
expect(BigInt(txResult.ctHash)).toBe(BigInt(privateCtHash));
|
|
184
|
+
expect(txResult.decryptedValue).toBe(privateValue);
|
|
185
|
+
}, 180000);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe('verifyDecryptResult', () => {
|
|
189
|
+
it('should correctly verify a valid decrypt result', async () => {
|
|
190
|
+
const permit = await createPermit();
|
|
191
|
+
|
|
192
|
+
const decryptResult = await txBuilder(privateCtHash).withPermit(permit).execute();
|
|
193
|
+
|
|
194
|
+
const isValid = await verifyDecryptResult(
|
|
195
|
+
decryptResult.ctHash,
|
|
196
|
+
privateValue,
|
|
197
|
+
decryptResult.signature,
|
|
198
|
+
publicClient
|
|
199
|
+
);
|
|
200
|
+
expect(isValid).toBe(true);
|
|
201
|
+
}, 180000);
|
|
202
|
+
|
|
203
|
+
it('should verify identically to the on-chain TaskManager contract', async () => {
|
|
204
|
+
const permit = await createPermit();
|
|
205
|
+
const decryptResult = await txBuilder(privateCtHash).withPermit(permit).execute();
|
|
206
|
+
|
|
207
|
+
const samples = [
|
|
208
|
+
{
|
|
209
|
+
shouldBe: true,
|
|
210
|
+
handle: BigInt(decryptResult.ctHash),
|
|
211
|
+
cleartext: decryptResult.decryptedValue,
|
|
212
|
+
signature: decryptResult.signature,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
shouldBe: false,
|
|
216
|
+
handle: BigInt(decryptResult.ctHash),
|
|
217
|
+
cleartext: decryptResult.decryptedValue + 1n,
|
|
218
|
+
signature: decryptResult.signature,
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
shouldBe: false,
|
|
222
|
+
handle: BigInt(decryptResult.ctHash) + 1n,
|
|
223
|
+
cleartext: decryptResult.decryptedValue,
|
|
224
|
+
signature: decryptResult.signature,
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
shouldBe: false,
|
|
228
|
+
handle: BigInt(decryptResult.ctHash),
|
|
229
|
+
cleartext: decryptResult.decryptedValue,
|
|
230
|
+
signature: `${decryptResult.signature}00`,
|
|
231
|
+
},
|
|
232
|
+
] as const;
|
|
233
|
+
|
|
234
|
+
for (const sample of samples) {
|
|
235
|
+
const sdkResult = await verifyDecryptResult(sample.handle, sample.cleartext, sample.signature, publicClient);
|
|
236
|
+
|
|
237
|
+
const verifyDecryptResultSafeAbi = parseAbi([
|
|
238
|
+
'function verifyDecryptResultSafe(uint256 ctHash, uint256 cleartext, bytes signature) view returns (bool)',
|
|
239
|
+
]);
|
|
240
|
+
const tmResult = await publicClient.readContract({
|
|
241
|
+
address: TASK_MANAGER_ADDRESS,
|
|
242
|
+
abi: verifyDecryptResultSafeAbi,
|
|
243
|
+
functionName: 'verifyDecryptResultSafe',
|
|
244
|
+
args: [sample.handle, sample.cleartext, sample.signature],
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
expect(sdkResult).to.equal(tmResult);
|
|
248
|
+
expect(sdkResult).to.equal(sample.shouldBe);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { type PublicClient, type WalletClient, createPublicClient, createWalletClient, http } from 'viem';
|
|
3
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
4
|
+
import { arbitrumSepolia } from 'viem/chains';
|
|
5
|
+
import { DecryptForTxBuilder } from '../decrypt/decryptForTxBuilder.js';
|
|
6
|
+
import { DecryptForViewBuilder } from '../decrypt/decryptForViewBuilder.js';
|
|
7
|
+
import { createCofheConfigBase, type CofheConfig } from '../config.js';
|
|
8
|
+
import { CofheErrorCode } from '../error.js';
|
|
9
|
+
import { FheTypes } from '../types.js';
|
|
10
|
+
|
|
11
|
+
const TEST_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
|
|
12
|
+
const account = privateKeyToAccount(TEST_PRIVATE_KEY);
|
|
13
|
+
const TEST_CHAIN_ID = 421614;
|
|
14
|
+
const TEST_CT_HASH = '0xabcdef1234567890';
|
|
15
|
+
|
|
16
|
+
const MockCoFheUrl = 'http://localhost:3001';
|
|
17
|
+
|
|
18
|
+
const publicClient: PublicClient = createPublicClient({
|
|
19
|
+
chain: arbitrumSepolia,
|
|
20
|
+
transport: http(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const walletClient: WalletClient = createWalletClient({
|
|
24
|
+
chain: arbitrumSepolia,
|
|
25
|
+
transport: http(),
|
|
26
|
+
account,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const mockConfig: CofheConfig = createCofheConfigBase({
|
|
30
|
+
supportedChains: [
|
|
31
|
+
{
|
|
32
|
+
id: TEST_CHAIN_ID,
|
|
33
|
+
name: 'Mock Chain',
|
|
34
|
+
network: 'Mock Network',
|
|
35
|
+
coFheUrl: MockCoFheUrl,
|
|
36
|
+
thresholdNetworkUrl: MockCoFheUrl,
|
|
37
|
+
environment: 'TESTNET',
|
|
38
|
+
verifierUrl: MockCoFheUrl,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function createTxBuilder(overrides?: Partial<{ chainId: number; account: string; ctHash: string | bigint }>) {
|
|
44
|
+
return new DecryptForTxBuilder({
|
|
45
|
+
config: mockConfig,
|
|
46
|
+
publicClient,
|
|
47
|
+
walletClient,
|
|
48
|
+
chainId: TEST_CHAIN_ID,
|
|
49
|
+
account: account.address,
|
|
50
|
+
ctHash: TEST_CT_HASH,
|
|
51
|
+
requireConnected: undefined,
|
|
52
|
+
...overrides,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function createViewBuilder<U extends FheTypes>(
|
|
57
|
+
utype: U,
|
|
58
|
+
overrides?: Partial<{ chainId: number; account: string; ctHash: string | bigint }>
|
|
59
|
+
) {
|
|
60
|
+
return new DecryptForViewBuilder<U>({
|
|
61
|
+
config: mockConfig,
|
|
62
|
+
publicClient,
|
|
63
|
+
walletClient,
|
|
64
|
+
chainId: TEST_CHAIN_ID,
|
|
65
|
+
account: account.address,
|
|
66
|
+
ctHash: TEST_CT_HASH,
|
|
67
|
+
utype,
|
|
68
|
+
requireConnected: undefined,
|
|
69
|
+
...overrides,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// DecryptForTxBuilder
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
describe('DecryptForTxBuilder', () => {
|
|
78
|
+
// --- setChainId / getChainId ---
|
|
79
|
+
|
|
80
|
+
describe('setChainId / getChainId', () => {
|
|
81
|
+
it('should store and return the chainId', () => {
|
|
82
|
+
const builder = createTxBuilder({ chainId: undefined });
|
|
83
|
+
expect(builder.getChainId()).toBeUndefined();
|
|
84
|
+
|
|
85
|
+
builder.setChainId(11155111);
|
|
86
|
+
expect(builder.getChainId()).toBe(11155111);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should allow overriding', () => {
|
|
90
|
+
const builder = createTxBuilder({ chainId: 1 });
|
|
91
|
+
builder.setChainId(42);
|
|
92
|
+
expect(builder.getChainId()).toBe(42);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// --- setAccount / getAccount ---
|
|
97
|
+
|
|
98
|
+
describe('setAccount / getAccount', () => {
|
|
99
|
+
it('should store and return the account', () => {
|
|
100
|
+
const builder = createTxBuilder({ account: undefined });
|
|
101
|
+
expect(builder.getAccount()).toBeUndefined();
|
|
102
|
+
|
|
103
|
+
builder.setAccount('0xdeadbeef');
|
|
104
|
+
expect(builder.getAccount()).toBe('0xdeadbeef');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should allow overriding', () => {
|
|
108
|
+
const builder = createTxBuilder();
|
|
109
|
+
builder.setAccount('0xnewaccount');
|
|
110
|
+
expect(builder.getAccount()).toBe('0xnewaccount');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// --- withPermit / withoutPermit selection ---
|
|
115
|
+
|
|
116
|
+
describe('withPermit / withoutPermit selection', () => {
|
|
117
|
+
it('withPermit() should set permit selection', () => {
|
|
118
|
+
const builder = createTxBuilder();
|
|
119
|
+
const selected = builder.withPermit();
|
|
120
|
+
expect(selected).toBeDefined();
|
|
121
|
+
expect(selected.getPermit()).toBeUndefined();
|
|
122
|
+
expect(selected.getPermitHash()).toBeUndefined();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('withPermit(hash) should store the permit hash', () => {
|
|
126
|
+
const builder = createTxBuilder();
|
|
127
|
+
const selected = builder.withPermit('0xmypermithash');
|
|
128
|
+
expect(selected.getPermitHash()).toBe('0xmypermithash');
|
|
129
|
+
expect(selected.getPermit()).toBeUndefined();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('withoutPermit() should set permit selection', () => {
|
|
133
|
+
const builder = createTxBuilder();
|
|
134
|
+
const selected = builder.withoutPermit();
|
|
135
|
+
expect(selected).toBeDefined();
|
|
136
|
+
expect(selected.getPermit()).toBeUndefined();
|
|
137
|
+
expect(selected.getPermitHash()).toBeUndefined();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should throw when withPermit() is called twice', () => {
|
|
141
|
+
const builder = createTxBuilder();
|
|
142
|
+
builder.withPermit();
|
|
143
|
+
|
|
144
|
+
expect(() => (builder as any).withPermit()).toThrow('withPermit() can only be selected once');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should throw when withoutPermit() is called twice', () => {
|
|
148
|
+
const builder = createTxBuilder();
|
|
149
|
+
builder.withoutPermit();
|
|
150
|
+
|
|
151
|
+
expect(() => (builder as any).withoutPermit()).toThrow('withoutPermit() can only be selected once');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should throw when withPermit() is called after withoutPermit()', () => {
|
|
155
|
+
const builder = createTxBuilder();
|
|
156
|
+
builder.withoutPermit();
|
|
157
|
+
|
|
158
|
+
expect(() => (builder as any).withPermit()).toThrow('cannot call withPermit() after withoutPermit()');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should throw when withoutPermit() is called after withPermit()', () => {
|
|
162
|
+
const builder = createTxBuilder();
|
|
163
|
+
builder.withPermit();
|
|
164
|
+
|
|
165
|
+
expect(() => (builder as any).withoutPermit()).toThrow('cannot call withoutPermit() after withPermit()');
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// --- chaining ---
|
|
170
|
+
|
|
171
|
+
describe('chaining', () => {
|
|
172
|
+
it('should return the builder from each setter for fluent chaining', () => {
|
|
173
|
+
const builder = createTxBuilder({ chainId: undefined, account: undefined });
|
|
174
|
+
const result = builder.setChainId(TEST_CHAIN_ID).setAccount(account.address).withPermit();
|
|
175
|
+
|
|
176
|
+
expect(result).toBeDefined();
|
|
177
|
+
expect(result.getChainId()).toBe(TEST_CHAIN_ID);
|
|
178
|
+
expect(result.getAccount()).toBe(account.address);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should allow setChainId and setAccount after withPermit', () => {
|
|
182
|
+
const builder = createTxBuilder({ chainId: undefined, account: undefined });
|
|
183
|
+
const selected = builder.withPermit();
|
|
184
|
+
selected.setChainId(99);
|
|
185
|
+
selected.setAccount('0xabc');
|
|
186
|
+
|
|
187
|
+
expect(selected.getChainId()).toBe(99);
|
|
188
|
+
expect(selected.getAccount()).toBe('0xabc');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should allow setChainId and setAccount after withoutPermit', () => {
|
|
192
|
+
const builder = createTxBuilder({ chainId: undefined, account: undefined });
|
|
193
|
+
const selected = builder.withoutPermit();
|
|
194
|
+
selected.setChainId(99);
|
|
195
|
+
selected.setAccount('0xabc');
|
|
196
|
+
|
|
197
|
+
expect(selected.getChainId()).toBe(99);
|
|
198
|
+
expect(selected.getAccount()).toBe('0xabc');
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// --- execute error paths ---
|
|
203
|
+
|
|
204
|
+
describe('execute – error paths', () => {
|
|
205
|
+
it('should throw when execute() is called without permit selection', async () => {
|
|
206
|
+
const builder = createTxBuilder();
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
await builder.execute();
|
|
210
|
+
expect.fail('Expected error');
|
|
211
|
+
} catch (error) {
|
|
212
|
+
expect((error as any).code).toBe(CofheErrorCode.InternalError);
|
|
213
|
+
expect((error as Error).message).toContain('missing permit selection');
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should throw when withPermit() has no active permit', async () => {
|
|
218
|
+
const builder = createTxBuilder();
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
await builder.withPermit().execute();
|
|
222
|
+
expect.fail('Expected PermitNotFound error');
|
|
223
|
+
} catch (error) {
|
|
224
|
+
expect((error as any).code).toBe(CofheErrorCode.PermitNotFound);
|
|
225
|
+
expect((error as Error).message).toContain('Active permit not found');
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should throw when withPermit(hash) cannot find permit', async () => {
|
|
230
|
+
const builder = createTxBuilder();
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
await builder.withPermit('0xnonexistent').execute();
|
|
234
|
+
expect.fail('Expected PermitNotFound error');
|
|
235
|
+
} catch (error) {
|
|
236
|
+
expect((error as any).code).toBe(CofheErrorCode.PermitNotFound);
|
|
237
|
+
expect((error as Error).message).toContain('Permit with hash');
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// --- constructor error paths ---
|
|
243
|
+
|
|
244
|
+
describe('constructor – error paths', () => {
|
|
245
|
+
it('should throw when config is undefined', () => {
|
|
246
|
+
expect(
|
|
247
|
+
() =>
|
|
248
|
+
new DecryptForTxBuilder({
|
|
249
|
+
config: undefined,
|
|
250
|
+
publicClient,
|
|
251
|
+
walletClient,
|
|
252
|
+
chainId: TEST_CHAIN_ID,
|
|
253
|
+
account: account.address,
|
|
254
|
+
ctHash: TEST_CT_HASH,
|
|
255
|
+
requireConnected: undefined,
|
|
256
|
+
})
|
|
257
|
+
).toThrow();
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
// DecryptForViewBuilder
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
|
|
266
|
+
describe('DecryptForViewBuilder', () => {
|
|
267
|
+
// --- setChainId / getChainId ---
|
|
268
|
+
|
|
269
|
+
describe('setChainId / getChainId', () => {
|
|
270
|
+
it('should store and return the chainId', () => {
|
|
271
|
+
const builder = createViewBuilder(FheTypes.Uint32, { chainId: undefined });
|
|
272
|
+
expect(builder.getChainId()).toBeUndefined();
|
|
273
|
+
|
|
274
|
+
builder.setChainId(11155111);
|
|
275
|
+
expect(builder.getChainId()).toBe(11155111);
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// --- setAccount / getAccount ---
|
|
280
|
+
|
|
281
|
+
describe('setAccount / getAccount', () => {
|
|
282
|
+
it('should store and return the account', () => {
|
|
283
|
+
const builder = createViewBuilder(FheTypes.Uint32, { account: undefined });
|
|
284
|
+
expect(builder.getAccount()).toBeUndefined();
|
|
285
|
+
|
|
286
|
+
builder.setAccount('0xdeadbeef');
|
|
287
|
+
expect(builder.getAccount()).toBe('0xdeadbeef');
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// --- withPermit ---
|
|
292
|
+
|
|
293
|
+
describe('withPermit', () => {
|
|
294
|
+
it('withPermit() should clear permit and hash', () => {
|
|
295
|
+
const builder = createViewBuilder(FheTypes.Uint32);
|
|
296
|
+
builder.withPermit();
|
|
297
|
+
expect(builder.getPermit()).toBeUndefined();
|
|
298
|
+
expect(builder.getPermitHash()).toBeUndefined();
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('withPermit(hash) should store the permit hash', () => {
|
|
302
|
+
const builder = createViewBuilder(FheTypes.Uint32);
|
|
303
|
+
builder.withPermit('0xmypermithash');
|
|
304
|
+
expect(builder.getPermitHash()).toBe('0xmypermithash');
|
|
305
|
+
expect(builder.getPermit()).toBeUndefined();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should allow overriding permit selection', () => {
|
|
309
|
+
const builder = createViewBuilder(FheTypes.Uint32);
|
|
310
|
+
builder.withPermit('0xfirst');
|
|
311
|
+
expect(builder.getPermitHash()).toBe('0xfirst');
|
|
312
|
+
|
|
313
|
+
builder.withPermit('0xsecond');
|
|
314
|
+
expect(builder.getPermitHash()).toBe('0xsecond');
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// --- chaining ---
|
|
319
|
+
|
|
320
|
+
describe('chaining', () => {
|
|
321
|
+
it('should return the builder from each setter for fluent chaining', () => {
|
|
322
|
+
const builder = createViewBuilder(FheTypes.Uint32, { chainId: undefined, account: undefined });
|
|
323
|
+
const result = builder.setChainId(TEST_CHAIN_ID).setAccount(account.address).withPermit();
|
|
324
|
+
|
|
325
|
+
expect(result).toBeDefined();
|
|
326
|
+
expect(result.getChainId()).toBe(TEST_CHAIN_ID);
|
|
327
|
+
expect(result.getAccount()).toBe(account.address);
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// --- execute error paths ---
|
|
332
|
+
|
|
333
|
+
describe('execute – error paths', () => {
|
|
334
|
+
it('should throw when active permit is not found', async () => {
|
|
335
|
+
const builder = createViewBuilder(FheTypes.Uint32);
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
await builder.execute();
|
|
339
|
+
expect.fail('Expected PermitNotFound error');
|
|
340
|
+
} catch (error) {
|
|
341
|
+
expect((error as any).code).toBe(CofheErrorCode.PermitNotFound);
|
|
342
|
+
expect((error as Error).message).toContain('Active permit not found');
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('should throw when withPermit(hash) cannot find permit', async () => {
|
|
347
|
+
const builder = createViewBuilder(FheTypes.Uint32);
|
|
348
|
+
builder.withPermit('0xnonexistent');
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
await builder.execute();
|
|
352
|
+
expect.fail('Expected PermitNotFound error');
|
|
353
|
+
} catch (error) {
|
|
354
|
+
expect((error as any).code).toBe(CofheErrorCode.PermitNotFound);
|
|
355
|
+
expect((error as Error).message).toContain('Permit with hash');
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('should throw for invalid utype', async () => {
|
|
360
|
+
const builder = createViewBuilder(999 as FheTypes);
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
await builder.execute();
|
|
364
|
+
expect.fail('Expected InvalidUtype error');
|
|
365
|
+
} catch (error) {
|
|
366
|
+
expect((error as any).code).toBe(CofheErrorCode.InvalidUtype);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// --- constructor error paths ---
|
|
372
|
+
|
|
373
|
+
describe('constructor – error paths', () => {
|
|
374
|
+
it('should throw when config is undefined', () => {
|
|
375
|
+
expect(
|
|
376
|
+
() =>
|
|
377
|
+
new DecryptForViewBuilder({
|
|
378
|
+
config: undefined,
|
|
379
|
+
publicClient,
|
|
380
|
+
walletClient,
|
|
381
|
+
chainId: TEST_CHAIN_ID,
|
|
382
|
+
account: account.address,
|
|
383
|
+
ctHash: TEST_CT_HASH,
|
|
384
|
+
utype: FheTypes.Uint32,
|
|
385
|
+
requireConnected: undefined,
|
|
386
|
+
})
|
|
387
|
+
).toThrow();
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
});
|