@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
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { Encryptable, FheTypes, type CofheClient } from '@/core';
|
|
2
|
+
import { arbSepolia as cofheArbSepolia, 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 { describe, it, expect, beforeAll, beforeEach } from 'vitest';
|
|
11
|
+
import type { Chain, PublicClient, WalletClient } from 'viem';
|
|
12
|
+
import { createPublicClient, createWalletClient, http } from 'viem';
|
|
13
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
14
|
+
import { arbitrumSepolia, baseSepolia, sepolia } from 'viem/chains';
|
|
15
|
+
import { createCofheClient, createCofheConfig } from '../index.js';
|
|
16
|
+
|
|
17
|
+
const DEFAULT_TEST_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
|
|
18
|
+
const BOB_PRIVATE_KEY = (TEST_PRIVATE_KEY || DEFAULT_TEST_PRIVATE_KEY) as `0x${string}`;
|
|
19
|
+
const ALICE_PRIVATE_KEY = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d';
|
|
20
|
+
|
|
21
|
+
const bobAccount = privateKeyToAccount(BOB_PRIVATE_KEY);
|
|
22
|
+
const aliceAccount = privateKeyToAccount(ALICE_PRIVATE_KEY);
|
|
23
|
+
|
|
24
|
+
const VIEM_CHAINS: Record<number, Chain> = {
|
|
25
|
+
421614: arbitrumSepolia,
|
|
26
|
+
84532: baseSepolia,
|
|
27
|
+
11155111: sepolia,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
describe('@cofhe/node - Inherited Client Tests', () => {
|
|
31
|
+
let cofheClient: CofheClient;
|
|
32
|
+
let publicClient: PublicClient;
|
|
33
|
+
let bobWalletClient: WalletClient;
|
|
34
|
+
let aliceWalletClient: WalletClient;
|
|
35
|
+
|
|
36
|
+
beforeAll(() => {
|
|
37
|
+
publicClient = createPublicClient({
|
|
38
|
+
chain: arbitrumSepolia,
|
|
39
|
+
transport: http(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
bobWalletClient = createWalletClient({
|
|
43
|
+
chain: arbitrumSepolia,
|
|
44
|
+
transport: http(),
|
|
45
|
+
account: bobAccount,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
aliceWalletClient = createWalletClient({
|
|
49
|
+
chain: arbitrumSepolia,
|
|
50
|
+
transport: http(),
|
|
51
|
+
account: aliceAccount,
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
const config = createCofheConfig({
|
|
57
|
+
supportedChains: [cofheArbSepolia],
|
|
58
|
+
});
|
|
59
|
+
cofheClient = createCofheClient(config);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('Client Creation', () => {
|
|
63
|
+
it('should create a client with expected surface', () => {
|
|
64
|
+
expect(cofheClient).toBeDefined();
|
|
65
|
+
expect(cofheClient.config).toBeDefined();
|
|
66
|
+
expect(cofheClient.connected).toBe(false);
|
|
67
|
+
expect(typeof cofheClient.connect).toBe('function');
|
|
68
|
+
expect(typeof cofheClient.disconnect).toBe('function');
|
|
69
|
+
expect(typeof cofheClient.encryptInputs).toBe('function');
|
|
70
|
+
expect(typeof cofheClient.decryptForView).toBe('function');
|
|
71
|
+
expect(typeof cofheClient.decryptForTx).toBe('function');
|
|
72
|
+
expect(typeof cofheClient.getSnapshot).toBe('function');
|
|
73
|
+
expect(typeof cofheClient.subscribe).toBe('function');
|
|
74
|
+
expect(cofheClient.permits).toBeDefined();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('Connection', () => {
|
|
79
|
+
it('should connect to a real chain', async () => {
|
|
80
|
+
await cofheClient.connect(publicClient, bobWalletClient);
|
|
81
|
+
|
|
82
|
+
expect(cofheClient.connected).toBe(true);
|
|
83
|
+
|
|
84
|
+
const snapshot = cofheClient.getSnapshot();
|
|
85
|
+
expect(snapshot.connected).toBe(true);
|
|
86
|
+
expect(snapshot.chainId).toBe(cofheArbSepolia.id);
|
|
87
|
+
expect(snapshot.account).toBe(bobAccount.address);
|
|
88
|
+
}, 30000);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('Encrypt Input', () => {
|
|
92
|
+
it('should encrypt a uint128 value', async () => {
|
|
93
|
+
await cofheClient.connect(publicClient, bobWalletClient);
|
|
94
|
+
|
|
95
|
+
const encrypted = await cofheClient.encryptInputs([Encryptable.uint128(100n)]).execute();
|
|
96
|
+
|
|
97
|
+
expect(encrypted).toBeDefined();
|
|
98
|
+
expect(encrypted.length).toBe(1);
|
|
99
|
+
expect(encrypted[0].utype).toBe(FheTypes.Uint128);
|
|
100
|
+
expect(encrypted[0].ctHash).toBeDefined();
|
|
101
|
+
expect(typeof encrypted[0].ctHash).toBe('bigint');
|
|
102
|
+
expect(encrypted[0].signature).toBeDefined();
|
|
103
|
+
expect(typeof encrypted[0].signature).toBe('string');
|
|
104
|
+
expect(encrypted[0].securityZone).toBe(0);
|
|
105
|
+
}, 60000);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('Self Permit', () => {
|
|
109
|
+
it('should create a self permit', async () => {
|
|
110
|
+
await cofheClient.connect(publicClient, bobWalletClient);
|
|
111
|
+
|
|
112
|
+
const permit = await cofheClient.permits.createSelf({
|
|
113
|
+
issuer: bobAccount.address,
|
|
114
|
+
name: 'Test Self Permit',
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(permit).toBeDefined();
|
|
118
|
+
expect(permit.type).toBe('self');
|
|
119
|
+
expect(permit.name).toBe('Test Self Permit');
|
|
120
|
+
expect(permit.issuer).toBe(bobAccount.address);
|
|
121
|
+
expect(permit.issuerSignature).not.toBe('0x');
|
|
122
|
+
expect(permit.sealingPair).toBeDefined();
|
|
123
|
+
expect(permit.sealingPair.publicKey).toBeDefined();
|
|
124
|
+
|
|
125
|
+
const activePermit = cofheClient.permits.getActivePermit();
|
|
126
|
+
expect(activePermit).toBeDefined();
|
|
127
|
+
expect(activePermit!.hash).toBe(permit.hash);
|
|
128
|
+
}, 30000);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('Sharing Permit', () => {
|
|
132
|
+
it('should create a sharing permit, export it, and import it as another user', async () => {
|
|
133
|
+
await cofheClient.connect(publicClient, bobWalletClient);
|
|
134
|
+
|
|
135
|
+
const sharingPermit = await cofheClient.permits.createSharing({
|
|
136
|
+
issuer: bobAccount.address,
|
|
137
|
+
recipient: aliceAccount.address,
|
|
138
|
+
name: 'Test Sharing Permit',
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
expect(sharingPermit).toBeDefined();
|
|
142
|
+
expect(sharingPermit.type).toBe('sharing');
|
|
143
|
+
expect(sharingPermit.issuer).toBe(bobAccount.address);
|
|
144
|
+
expect(sharingPermit.recipient).toBe(aliceAccount.address);
|
|
145
|
+
expect(sharingPermit.issuerSignature).not.toBe('0x');
|
|
146
|
+
|
|
147
|
+
const exported = cofheClient.permits.export(sharingPermit);
|
|
148
|
+
expect(exported).toBeDefined();
|
|
149
|
+
const parsed = JSON.parse(exported);
|
|
150
|
+
expect(parsed.type).toBe('sharing');
|
|
151
|
+
expect(parsed.issuer).toBe(bobAccount.address);
|
|
152
|
+
expect(parsed.recipient).toBe(aliceAccount.address);
|
|
153
|
+
expect(parsed.issuerSignature).toBeDefined();
|
|
154
|
+
expect(parsed).not.toHaveProperty('sealingPair');
|
|
155
|
+
|
|
156
|
+
const aliceConfig = createCofheConfig({
|
|
157
|
+
supportedChains: [cofheArbSepolia],
|
|
158
|
+
});
|
|
159
|
+
const aliceClient = createCofheClient(aliceConfig);
|
|
160
|
+
await aliceClient.connect(publicClient, aliceWalletClient);
|
|
161
|
+
|
|
162
|
+
const importedPermit = await aliceClient.permits.importShared(exported);
|
|
163
|
+
|
|
164
|
+
expect(importedPermit).toBeDefined();
|
|
165
|
+
expect(importedPermit.type).toBe('recipient');
|
|
166
|
+
expect(importedPermit.issuer).toBe(bobAccount.address);
|
|
167
|
+
expect(importedPermit.recipient).toBe(aliceAccount.address);
|
|
168
|
+
expect(importedPermit.recipientSignature).not.toBe('0x');
|
|
169
|
+
expect(importedPermit.sealingPair).toBeDefined();
|
|
170
|
+
}, 30000);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe('Decrypt (read-only, pre-stored values)', () => {
|
|
174
|
+
let decryptClient: CofheClient;
|
|
175
|
+
let decryptPublicClient: PublicClient;
|
|
176
|
+
let decryptWalletClient: WalletClient;
|
|
177
|
+
|
|
178
|
+
let privateCtHash: `0x${string}`;
|
|
179
|
+
let privateValue: bigint;
|
|
180
|
+
let publicCtHash: `0x${string}`;
|
|
181
|
+
let publicValue: bigint;
|
|
182
|
+
|
|
183
|
+
beforeAll(() => {
|
|
184
|
+
if (!isPrimaryTestChainReady(primaryTestChainRegistry)) {
|
|
185
|
+
throw new Error('Primary test chain registry not initialized. Run `pnpm test:setup` first.');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const reg = primaryTestChainRegistry;
|
|
189
|
+
const viemChain = VIEM_CHAINS[reg.chainId];
|
|
190
|
+
if (!viemChain) throw new Error(`No viem chain mapping for chain ${reg.chainId}`);
|
|
191
|
+
|
|
192
|
+
const cofheChain = getChainById(reg.chainId);
|
|
193
|
+
if (!cofheChain) throw new Error(`No cofhe chain config for chain ${reg.chainId}`);
|
|
194
|
+
|
|
195
|
+
privateCtHash = reg.privateValue.ctHash as `0x${string}`;
|
|
196
|
+
privateValue = BigInt(reg.privateValue.value);
|
|
197
|
+
publicCtHash = reg.publicValue.ctHash as `0x${string}`;
|
|
198
|
+
publicValue = BigInt(reg.publicValue.value);
|
|
199
|
+
|
|
200
|
+
decryptPublicClient = createPublicClient({ chain: viemChain, transport: http() });
|
|
201
|
+
decryptWalletClient = createWalletClient({ chain: viemChain, transport: http(), account: bobAccount });
|
|
202
|
+
|
|
203
|
+
const config = createCofheConfig({ supportedChains: [cofheChain] });
|
|
204
|
+
decryptClient = createCofheClient(config);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('decryptForView — private value with permit', async () => {
|
|
208
|
+
await decryptClient.connect(decryptPublicClient, decryptWalletClient);
|
|
209
|
+
|
|
210
|
+
await decryptClient.permits.createSelf({
|
|
211
|
+
issuer: bobAccount.address,
|
|
212
|
+
name: 'Decrypt View Permit',
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const result = await decryptClient.decryptForView(privateCtHash, FheTypes.Uint32).execute();
|
|
216
|
+
expect(result).toBe(privateValue);
|
|
217
|
+
}, 180000);
|
|
218
|
+
|
|
219
|
+
it('decryptForTx — public value without permit', async () => {
|
|
220
|
+
await decryptClient.connect(decryptPublicClient, decryptWalletClient);
|
|
221
|
+
|
|
222
|
+
const result = await decryptClient.decryptForTx(publicCtHash).withoutPermit().execute();
|
|
223
|
+
|
|
224
|
+
expect(BigInt(result.ctHash)).toBe(BigInt(publicCtHash));
|
|
225
|
+
expect(result.decryptedValue).toBe(publicValue);
|
|
226
|
+
expect(result.signature).toMatch(/^0x[0-9a-fA-F]+$/);
|
|
227
|
+
}, 180000);
|
|
228
|
+
|
|
229
|
+
it('decryptForTx — private value with permit', async () => {
|
|
230
|
+
await decryptClient.connect(decryptPublicClient, decryptWalletClient);
|
|
231
|
+
|
|
232
|
+
const permit = await decryptClient.permits.createSelf({
|
|
233
|
+
issuer: bobAccount.address,
|
|
234
|
+
name: 'Decrypt Tx Permit',
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const result = await decryptClient.decryptForTx(privateCtHash).withPermit(permit).execute();
|
|
238
|
+
|
|
239
|
+
expect(BigInt(result.ctHash)).toBe(BigInt(privateCtHash));
|
|
240
|
+
expect(result.decryptedValue).toBe(privateValue);
|
|
241
|
+
expect(result.signature).toBeDefined();
|
|
242
|
+
}, 180000);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Encryptable, type CofheClient } from '@/core';
|
|
2
|
+
import { arbSepolia as cofheArbSepolia } from '@/chains';
|
|
3
|
+
|
|
4
|
+
import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
|
|
5
|
+
import type { PublicClient, WalletClient } from 'viem';
|
|
6
|
+
import { createPublicClient, createWalletClient, http } from 'viem';
|
|
7
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
8
|
+
import { arbitrumSepolia as viemArbitrumSepolia } from 'viem/chains';
|
|
9
|
+
import { createCofheClient, createCofheConfig } from '../index.js';
|
|
10
|
+
|
|
11
|
+
const TEST_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
|
|
12
|
+
|
|
13
|
+
describe('@cofhe/node - TFHE Initialization Tests', () => {
|
|
14
|
+
let cofheClient: CofheClient;
|
|
15
|
+
let publicClient: PublicClient;
|
|
16
|
+
let walletClient: WalletClient;
|
|
17
|
+
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
publicClient = createPublicClient({
|
|
20
|
+
chain: viemArbitrumSepolia,
|
|
21
|
+
transport: http(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const account = privateKeyToAccount(TEST_PRIVATE_KEY);
|
|
25
|
+
walletClient = createWalletClient({
|
|
26
|
+
chain: viemArbitrumSepolia,
|
|
27
|
+
transport: http(),
|
|
28
|
+
account,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
const config = createCofheConfig({
|
|
34
|
+
supportedChains: [cofheArbSepolia],
|
|
35
|
+
});
|
|
36
|
+
cofheClient = createCofheClient(config);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('Node TFHE Initialization', () => {
|
|
40
|
+
it('should initialize node-tfhe on first encryption', async () => {
|
|
41
|
+
await cofheClient.connect(publicClient, walletClient);
|
|
42
|
+
|
|
43
|
+
const result = await cofheClient.encryptInputs([Encryptable.uint128(100n)]).execute();
|
|
44
|
+
|
|
45
|
+
expect(result).toBeDefined();
|
|
46
|
+
}, 60000);
|
|
47
|
+
|
|
48
|
+
it('should handle multiple encryptions without re-initializing', async () => {
|
|
49
|
+
await cofheClient.connect(publicClient, walletClient);
|
|
50
|
+
|
|
51
|
+
await cofheClient.encryptInputs([Encryptable.uint128(100n)]).execute();
|
|
52
|
+
|
|
53
|
+
await cofheClient.encryptInputs([Encryptable.uint64(50n)]).execute();
|
|
54
|
+
}, 120000);
|
|
55
|
+
});
|
|
56
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cofhe/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SDK for Fhenix COFHE coprocessor interaction",
|
|
6
6
|
"main": "./dist/core.cjs",
|
|
@@ -57,14 +57,14 @@
|
|
|
57
57
|
"CHANGELOG.md"
|
|
58
58
|
],
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"iframe-shared-storage": "
|
|
61
|
-
"immer": "
|
|
62
|
-
"node-tfhe": "
|
|
63
|
-
"tfhe": "
|
|
64
|
-
"tweetnacl": "
|
|
65
|
-
"viem": "
|
|
66
|
-
"zod": "
|
|
67
|
-
"zustand": "
|
|
60
|
+
"iframe-shared-storage": "1.0.34",
|
|
61
|
+
"immer": "10.1.1",
|
|
62
|
+
"node-tfhe": "1.5.3",
|
|
63
|
+
"tfhe": "1.5.3",
|
|
64
|
+
"tweetnacl": "1.0.3",
|
|
65
|
+
"viem": "2.38.6",
|
|
66
|
+
"zod": "4.0.0",
|
|
67
|
+
"zustand": "5.0.1"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"@nomicfoundation/hardhat-ethers": "^3.0.0",
|
|
@@ -88,24 +88,26 @@
|
|
|
88
88
|
}
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
91
|
-
"@nomicfoundation/hardhat-ethers": "
|
|
92
|
-
"@types/node": "
|
|
93
|
-
"@vitest/browser": "
|
|
94
|
-
"@vitest/coverage-v8": "
|
|
95
|
-
"eslint": "
|
|
96
|
-
"ethers5": "npm:ethers
|
|
97
|
-
"ethers6": "npm:ethers
|
|
98
|
-
"happy-dom": "
|
|
99
|
-
"hardhat": "
|
|
100
|
-
"playwright": "
|
|
101
|
-
"tsup": "
|
|
91
|
+
"@nomicfoundation/hardhat-ethers": "3.1.0",
|
|
92
|
+
"@types/node": "20.19.15",
|
|
93
|
+
"@vitest/browser": "3.2.4",
|
|
94
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
95
|
+
"eslint": "8.57.1",
|
|
96
|
+
"ethers5": "npm:ethers@5.8.0",
|
|
97
|
+
"ethers6": "npm:ethers@6.15.0",
|
|
98
|
+
"happy-dom": "15.11.7",
|
|
99
|
+
"hardhat": "2.26.3",
|
|
100
|
+
"playwright": "1.55.0",
|
|
101
|
+
"tsup": "8.0.2",
|
|
102
102
|
"typescript": "5.5.4",
|
|
103
|
-
"vitest": "
|
|
103
|
+
"vitest": "3.2.4",
|
|
104
104
|
"@cofhe/eslint-config": "0.2.1",
|
|
105
|
+
"@cofhe/test-setup": "0.0.0",
|
|
105
106
|
"@cofhe/tsconfig": "0.1.2"
|
|
106
107
|
},
|
|
107
108
|
"publishConfig": {
|
|
108
|
-
"access": "public"
|
|
109
|
+
"access": "public",
|
|
110
|
+
"registry": "https://registry.npmjs.org/"
|
|
109
111
|
},
|
|
110
112
|
"scripts": {
|
|
111
113
|
"build": "tsup",
|
package/permits/permit.ts
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
validateImportPermit,
|
|
23
23
|
ValidationUtils,
|
|
24
24
|
} from './validation.js';
|
|
25
|
-
import * as z from 'zod';
|
|
26
25
|
import { SignatureUtils } from './signature.js';
|
|
27
26
|
import { GenerateSealingKey, SealingKey } from './sealing.js';
|
|
28
27
|
import { checkPermitValidityOnChain, getAclEIP712Domain } from './onchain-utils.js';
|
|
@@ -217,9 +216,9 @@ export const PermitUtils = {
|
|
|
217
216
|
},
|
|
218
217
|
|
|
219
218
|
/**
|
|
220
|
-
* Validate a permit
|
|
219
|
+
* Validate a permit (schema-level validation)
|
|
221
220
|
*/
|
|
222
|
-
|
|
221
|
+
validateSchema: (permit: Permit) => {
|
|
223
222
|
if (permit.type === 'self') {
|
|
224
223
|
return validateSelfPermit(permit);
|
|
225
224
|
} else if (permit.type === 'sharing') {
|
|
@@ -231,12 +230,28 @@ export const PermitUtils = {
|
|
|
231
230
|
}
|
|
232
231
|
},
|
|
233
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Validate a permit (holistic validation).
|
|
235
|
+
*
|
|
236
|
+
* This validates:
|
|
237
|
+
* - Permit schema (shape + invariants)
|
|
238
|
+
* - Permit is signed
|
|
239
|
+
* - Permit is not expired
|
|
240
|
+
*
|
|
241
|
+
* For schema-only validation, use `validateSchema(permit)`.
|
|
242
|
+
*/
|
|
243
|
+
validate: (permit: Permit) => {
|
|
244
|
+
const validated = PermitUtils.validateSchema(permit);
|
|
245
|
+
ValidationUtils.assertSignedAndNotExpired(validated as Permit);
|
|
246
|
+
return validated;
|
|
247
|
+
},
|
|
248
|
+
|
|
234
249
|
/**
|
|
235
250
|
* Get the permission object from a permit (for use in contracts)
|
|
236
251
|
*/
|
|
237
252
|
getPermission: (permit: Permit, skipValidation = false): Permission => {
|
|
238
253
|
if (!skipValidation) {
|
|
239
|
-
PermitUtils.
|
|
254
|
+
PermitUtils.validateSchema(permit);
|
|
240
255
|
}
|
|
241
256
|
|
|
242
257
|
return {
|
|
@@ -308,8 +323,19 @@ export const PermitUtils = {
|
|
|
308
323
|
},
|
|
309
324
|
|
|
310
325
|
/**
|
|
311
|
-
* Check if permit is
|
|
326
|
+
* Check if permit is signed and not expired
|
|
312
327
|
*/
|
|
328
|
+
isSignedAndNotExpired: (permit: Permit) => {
|
|
329
|
+
return ValidationUtils.isSignedAndNotExpired(permit);
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Assert that permit is signed and not expired
|
|
334
|
+
*/
|
|
335
|
+
assertSignedAndNotExpired: (permit: Permit): void => {
|
|
336
|
+
return ValidationUtils.assertSignedAndNotExpired(permit);
|
|
337
|
+
},
|
|
338
|
+
|
|
313
339
|
isValid: (permit: Permit) => {
|
|
314
340
|
return ValidationUtils.isValid(permit);
|
|
315
341
|
},
|
package/permits/sealing.ts
CHANGED
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
setActivePermitHash,
|
|
12
12
|
PermitUtils,
|
|
13
13
|
permitStore,
|
|
14
|
-
} from '
|
|
15
|
-
import { createMockPermit } from '
|
|
14
|
+
} from '../index.js';
|
|
15
|
+
import { createMockPermit } from '../test-utils.js';
|
|
16
16
|
|
|
17
17
|
// Type declarations for happy-dom environment
|
|
18
18
|
declare const localStorage: {
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
type CreateSelfPermitOptions,
|
|
5
5
|
type CreateSharingPermitOptions,
|
|
6
6
|
type ImportSharedPermitOptions,
|
|
7
|
-
} from '
|
|
7
|
+
} from '../index.js';
|
|
8
8
|
import { createPublicClient, createWalletClient, http, type PublicClient, type WalletClient } from 'viem';
|
|
9
9
|
import { arbitrumSepolia } from 'viem/chains';
|
|
10
10
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
@@ -399,6 +399,29 @@ describe('PermitUtils Tests', () => {
|
|
|
399
399
|
expect(parsed).not.toHaveProperty('sealingPair');
|
|
400
400
|
expect(parsed).not.toHaveProperty('issuerSignature');
|
|
401
401
|
});
|
|
402
|
+
|
|
403
|
+
it('should export sharing permit data with recipient and issuerSignature', async () => {
|
|
404
|
+
const permit = await PermitUtils.createSharingAndSign(
|
|
405
|
+
{
|
|
406
|
+
issuer: bobAddress,
|
|
407
|
+
recipient: aliceAddress,
|
|
408
|
+
name: 'Test Sharing Permit',
|
|
409
|
+
},
|
|
410
|
+
publicClient,
|
|
411
|
+
bobWalletClient
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
const exported = PermitUtils.export(permit);
|
|
415
|
+
const parsed = JSON.parse(exported);
|
|
416
|
+
|
|
417
|
+
expect(parsed.name).toBe('Test Sharing Permit');
|
|
418
|
+
expect(parsed.type).toBe('sharing');
|
|
419
|
+
expect(parsed.issuer).toBe(bobAddress);
|
|
420
|
+
expect(parsed.recipient).toBe(aliceAddress);
|
|
421
|
+
expect(parsed.issuerSignature).toBeDefined();
|
|
422
|
+
expect(parsed.issuerSignature).not.toBe('0x');
|
|
423
|
+
expect(parsed).not.toHaveProperty('sealingPair');
|
|
424
|
+
});
|
|
402
425
|
});
|
|
403
426
|
|
|
404
427
|
describe('updateName', () => {
|
|
@@ -459,6 +482,17 @@ describe('PermitUtils Tests', () => {
|
|
|
459
482
|
expect(validation.valid).toBe(true);
|
|
460
483
|
expect(validation.error).toBeNull();
|
|
461
484
|
});
|
|
485
|
+
|
|
486
|
+
it('should throw on validate() for expired signed permit', async () => {
|
|
487
|
+
const expiredPermit = PermitUtils.createSelf({
|
|
488
|
+
issuer: bobAddress,
|
|
489
|
+
name: 'Expired Permit',
|
|
490
|
+
expiration: Math.floor(Date.now() / 1000) - 3600,
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
const signedExpiredPermit = await PermitUtils.sign(expiredPermit, publicClient, bobWalletClient);
|
|
494
|
+
expect(() => PermitUtils.validate(signedExpiredPermit)).toThrow('Permit is expired');
|
|
495
|
+
});
|
|
462
496
|
});
|
|
463
497
|
|
|
464
498
|
describe('real contract interactions', () => {
|
|
@@ -13,9 +13,9 @@ import {
|
|
|
13
13
|
getActivePermitHash,
|
|
14
14
|
setActivePermitHash,
|
|
15
15
|
PermitUtils,
|
|
16
|
-
} from '
|
|
16
|
+
} from '../index.js';
|
|
17
17
|
|
|
18
|
-
import { createMockPermit } from '
|
|
18
|
+
import { createMockPermit } from '../test-utils.js';
|
|
19
19
|
|
|
20
20
|
describe('Storage Tests', () => {
|
|
21
21
|
const chainId = 1;
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
type CreateSelfPermitOptions,
|
|
12
12
|
type CreateSharingPermitOptions,
|
|
13
13
|
type ImportSharedPermitOptions,
|
|
14
|
-
} from '
|
|
15
|
-
import { createMockPermit } from '
|
|
14
|
+
} from '../index.js';
|
|
15
|
+
import { createMockPermit } from '../test-utils.js';
|
|
16
16
|
|
|
17
17
|
describe('Validation Tests', () => {
|
|
18
18
|
describe('validateSelfPermitOptions', () => {
|
|
@@ -247,14 +247,14 @@ describe('Validation Tests', () => {
|
|
|
247
247
|
});
|
|
248
248
|
});
|
|
249
249
|
|
|
250
|
-
describe('
|
|
250
|
+
describe('isSignedAndNotExpired', () => {
|
|
251
251
|
it('should return valid for valid permit', async () => {
|
|
252
252
|
const permit = {
|
|
253
253
|
...(await createMockPermit()),
|
|
254
254
|
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
255
255
|
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
256
256
|
};
|
|
257
|
-
const result = ValidationUtils.
|
|
257
|
+
const result = ValidationUtils.isSignedAndNotExpired(permit);
|
|
258
258
|
expect(result.valid).toBe(true);
|
|
259
259
|
expect(result.error).toBeNull();
|
|
260
260
|
});
|
|
@@ -265,7 +265,7 @@ describe('Validation Tests', () => {
|
|
|
265
265
|
expiration: Math.floor(Date.now() / 1000) - 3600,
|
|
266
266
|
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
267
267
|
};
|
|
268
|
-
const result = ValidationUtils.
|
|
268
|
+
const result = ValidationUtils.isSignedAndNotExpired(permit);
|
|
269
269
|
expect(result.valid).toBe(false);
|
|
270
270
|
expect(result.error).toBe('expired');
|
|
271
271
|
});
|
|
@@ -276,10 +276,86 @@ describe('Validation Tests', () => {
|
|
|
276
276
|
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
277
277
|
issuerSignature: '0x' as `0x${string}`,
|
|
278
278
|
};
|
|
279
|
-
const result = ValidationUtils.
|
|
279
|
+
const result = ValidationUtils.isSignedAndNotExpired(permit);
|
|
280
280
|
expect(result.valid).toBe(false);
|
|
281
281
|
expect(result.error).toBe('not-signed');
|
|
282
282
|
});
|
|
283
283
|
});
|
|
284
|
+
|
|
285
|
+
describe('assertSignedAndNotExpired', () => {
|
|
286
|
+
it('should not throw for valid permit', async () => {
|
|
287
|
+
const permit = {
|
|
288
|
+
...(await createMockPermit()),
|
|
289
|
+
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
290
|
+
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
291
|
+
};
|
|
292
|
+
expect(() => ValidationUtils.assertSignedAndNotExpired(permit)).not.toThrow();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('should throw for expired permit', async () => {
|
|
296
|
+
const permit = {
|
|
297
|
+
...(await createMockPermit()),
|
|
298
|
+
expiration: Math.floor(Date.now() / 1000) - 3600,
|
|
299
|
+
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
300
|
+
};
|
|
301
|
+
expect(() => ValidationUtils.assertSignedAndNotExpired(permit)).toThrow('Permit is expired');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('should throw for unsigned permit', async () => {
|
|
305
|
+
const permit = {
|
|
306
|
+
...(await createMockPermit()),
|
|
307
|
+
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
308
|
+
issuerSignature: '0x' as `0x${string}`,
|
|
309
|
+
};
|
|
310
|
+
expect(() => ValidationUtils.assertSignedAndNotExpired(permit)).toThrow('Permit is not signed');
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('isValid', () => {
|
|
315
|
+
it('should return invalid-schema for schema-invalid permit', async () => {
|
|
316
|
+
const permit = {
|
|
317
|
+
...(await createMockPermit()),
|
|
318
|
+
type: 'self' as const,
|
|
319
|
+
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
320
|
+
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
321
|
+
// Self permits must have recipient == zeroAddress per schema.
|
|
322
|
+
recipient: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8' as `0x${string}`,
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const result = ValidationUtils.isValid(permit as unknown as Permit);
|
|
326
|
+
expect(result.valid).toBe(false);
|
|
327
|
+
expect(result.error).toBe('invalid-schema');
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should return expired for expired but otherwise schema-valid permit', async () => {
|
|
331
|
+
const permit = {
|
|
332
|
+
...(await createMockPermit()),
|
|
333
|
+
type: 'self' as const,
|
|
334
|
+
expiration: Math.floor(Date.now() / 1000) - 3600,
|
|
335
|
+
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
336
|
+
recipient: '0x0000000000000000000000000000000000000000' as `0x${string}`,
|
|
337
|
+
recipientSignature: '0x' as `0x${string}`,
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const result = ValidationUtils.isValid(permit as unknown as Permit);
|
|
341
|
+
expect(result.valid).toBe(false);
|
|
342
|
+
expect(result.error).toBe('expired');
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('should return valid for schema-valid, signed, non-expired permit', async () => {
|
|
346
|
+
const permit = {
|
|
347
|
+
...(await createMockPermit()),
|
|
348
|
+
type: 'self' as const,
|
|
349
|
+
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
350
|
+
issuerSignature: '0x1234567890abcdef' as `0x${string}`,
|
|
351
|
+
recipient: '0x0000000000000000000000000000000000000000' as `0x${string}`,
|
|
352
|
+
recipientSignature: '0x' as `0x${string}`,
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const result = ValidationUtils.isValid(permit as unknown as Permit);
|
|
356
|
+
expect(result.valid).toBe(true);
|
|
357
|
+
expect(result.error).toBeNull();
|
|
358
|
+
});
|
|
359
|
+
});
|
|
284
360
|
});
|
|
285
361
|
});
|