@cofhe/sdk 0.0.0-beta-20251027110729
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 +47 -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 +37 -0
- package/adapters/index.test.ts +25 -0
- package/adapters/index.ts +5 -0
- package/adapters/smartWallet.ts +91 -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/sepolia.ts +14 -0
- package/chains/chains.test.ts +49 -0
- package/chains/defineChain.ts +18 -0
- package/chains/index.ts +33 -0
- package/chains/types.ts +32 -0
- package/core/baseBuilder.ts +138 -0
- package/core/client.test.ts +298 -0
- package/core/client.ts +308 -0
- package/core/config.test.ts +224 -0
- package/core/config.ts +213 -0
- package/core/decrypt/MockQueryDecrypterAbi.ts +129 -0
- package/core/decrypt/cofheMocksSealOutput.ts +57 -0
- package/core/decrypt/decryptHandleBuilder.ts +281 -0
- package/core/decrypt/decryptUtils.ts +28 -0
- package/core/decrypt/tnSealOutput.ts +59 -0
- package/core/encrypt/MockZkVerifierAbi.ts +106 -0
- package/core/encrypt/cofheMocksZkVerifySign.ts +278 -0
- package/core/encrypt/encryptInputsBuilder.test.ts +735 -0
- package/core/encrypt/encryptInputsBuilder.ts +512 -0
- package/core/encrypt/encryptUtils.ts +64 -0
- package/core/encrypt/zkPackProveVerify.ts +273 -0
- package/core/error.ts +170 -0
- package/core/fetchKeys.test.ts +212 -0
- package/core/fetchKeys.ts +170 -0
- package/core/index.ts +77 -0
- package/core/keyStore.test.ts +226 -0
- package/core/keyStore.ts +127 -0
- package/core/permits.test.ts +242 -0
- package/core/permits.ts +136 -0
- package/core/result.test.ts +180 -0
- package/core/result.ts +67 -0
- package/core/test-utils.ts +45 -0
- package/core/types.ts +352 -0
- package/core/utils.ts +88 -0
- package/dist/adapters.cjs +88 -0
- package/dist/adapters.d.cts +14558 -0
- package/dist/adapters.d.ts +14558 -0
- package/dist/adapters.js +83 -0
- package/dist/chains.cjs +101 -0
- package/dist/chains.d.cts +99 -0
- package/dist/chains.d.ts +99 -0
- package/dist/chains.js +1 -0
- package/dist/chunk-GZCQQYVI.js +93 -0
- package/dist/chunk-KFGPTJ6X.js +2295 -0
- package/dist/chunk-LU7BMUUT.js +804 -0
- package/dist/core.cjs +3174 -0
- package/dist/core.d.cts +16 -0
- package/dist/core.d.ts +16 -0
- package/dist/core.js +3 -0
- package/dist/node.cjs +3090 -0
- package/dist/node.d.cts +22 -0
- package/dist/node.d.ts +22 -0
- package/dist/node.js +90 -0
- package/dist/permit-S9CnI6MF.d.cts +333 -0
- package/dist/permit-S9CnI6MF.d.ts +333 -0
- package/dist/permits.cjs +856 -0
- package/dist/permits.d.cts +1056 -0
- package/dist/permits.d.ts +1056 -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/types-PhwGgQvs.d.ts +953 -0
- package/dist/types-bB7wLj0q.d.cts +953 -0
- package/dist/web.cjs +3067 -0
- package/dist/web.d.cts +22 -0
- package/dist/web.d.ts +22 -0
- package/dist/web.js +64 -0
- package/node/client.test.ts +152 -0
- package/node/config.test.ts +68 -0
- package/node/encryptInputs.test.ts +175 -0
- package/node/index.ts +96 -0
- package/node/storage.ts +51 -0
- package/package.json +120 -0
- package/permits/index.ts +67 -0
- package/permits/localstorage.test.ts +118 -0
- package/permits/permit.test.ts +474 -0
- package/permits/permit.ts +396 -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 +168 -0
- package/permits/test-utils.ts +20 -0
- package/permits/types.ts +174 -0
- package/permits/utils.ts +63 -0
- package/permits/validation.test.ts +288 -0
- package/permits/validation.ts +349 -0
- package/web/client.web.test.ts +152 -0
- package/web/config.web.test.ts +71 -0
- package/web/encryptInputs.web.test.ts +195 -0
- package/web/index.ts +97 -0
- package/web/storage.ts +20 -0
package/dist/web.d.cts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { d as CofhesdkInputConfig, b as CofhesdkConfig, a as CofhesdkClient } from './types-bB7wLj0q.cjs';
|
|
2
|
+
import './permit-S9CnI6MF.cjs';
|
|
3
|
+
import 'zod';
|
|
4
|
+
import 'viem';
|
|
5
|
+
import './types-KImPrEIe.cjs';
|
|
6
|
+
import 'zustand/vanilla';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a CoFHE SDK configuration for web with IndexedDB storage as default
|
|
10
|
+
* @param config - The CoFHE SDK input configuration (fheKeyStorage will default to IndexedDB if not provided)
|
|
11
|
+
* @returns The CoFHE SDK configuration with web defaults applied
|
|
12
|
+
*/
|
|
13
|
+
declare function createCofhesdkConfig(config: CofhesdkInputConfig): CofhesdkConfig;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a CoFHE SDK client instance for web with TFHE automatically configured
|
|
16
|
+
* TFHE will be initialized automatically on first encryption - no manual setup required
|
|
17
|
+
* @param config - The CoFHE SDK configuration (use createCofhesdkConfig to create with web defaults)
|
|
18
|
+
* @returns The CoFHE SDK client instance
|
|
19
|
+
*/
|
|
20
|
+
declare function createCofhesdkClient(config: CofhesdkConfig): CofhesdkClient;
|
|
21
|
+
|
|
22
|
+
export { createCofhesdkClient, createCofhesdkConfig };
|
package/dist/web.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { d as CofhesdkInputConfig, b as CofhesdkConfig, a as CofhesdkClient } from './types-PhwGgQvs.js';
|
|
2
|
+
import './permit-S9CnI6MF.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
import 'viem';
|
|
5
|
+
import './types-KImPrEIe.js';
|
|
6
|
+
import 'zustand/vanilla';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a CoFHE SDK configuration for web with IndexedDB storage as default
|
|
10
|
+
* @param config - The CoFHE SDK input configuration (fheKeyStorage will default to IndexedDB if not provided)
|
|
11
|
+
* @returns The CoFHE SDK configuration with web defaults applied
|
|
12
|
+
*/
|
|
13
|
+
declare function createCofhesdkConfig(config: CofhesdkInputConfig): CofhesdkConfig;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a CoFHE SDK client instance for web with TFHE automatically configured
|
|
16
|
+
* TFHE will be initialized automatically on first encryption - no manual setup required
|
|
17
|
+
* @param config - The CoFHE SDK configuration (use createCofhesdkConfig to create with web defaults)
|
|
18
|
+
* @returns The CoFHE SDK client instance
|
|
19
|
+
*/
|
|
20
|
+
declare function createCofhesdkClient(config: CofhesdkConfig): CofhesdkClient;
|
|
21
|
+
|
|
22
|
+
export { createCofhesdkClient, createCofhesdkConfig };
|
package/dist/web.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createCofhesdkConfigBase, createCofhesdkClientBase } from './chunk-KFGPTJ6X.js';
|
|
2
|
+
import './chunk-GZCQQYVI.js';
|
|
3
|
+
import './chunk-LU7BMUUT.js';
|
|
4
|
+
import { get, set, del } from 'idb-keyval';
|
|
5
|
+
import init, { init_panic_hook, TfheCompactPublicKey, CompactPkeCrs, ProvenCompactCiphertextList } from 'tfhe';
|
|
6
|
+
|
|
7
|
+
var createWebStorage = () => {
|
|
8
|
+
return {
|
|
9
|
+
getItem: async (name) => {
|
|
10
|
+
return await get(name) || null;
|
|
11
|
+
},
|
|
12
|
+
setItem: async (name, value) => {
|
|
13
|
+
await set(name, value);
|
|
14
|
+
},
|
|
15
|
+
removeItem: async (name) => {
|
|
16
|
+
await del(name);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
var tfheInitialized = false;
|
|
21
|
+
async function initTfhe() {
|
|
22
|
+
if (tfheInitialized)
|
|
23
|
+
return false;
|
|
24
|
+
await init();
|
|
25
|
+
await init_panic_hook();
|
|
26
|
+
tfheInitialized = true;
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
var fromHexString = (hexString) => {
|
|
30
|
+
const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
|
|
31
|
+
const arr = cleanString.replace(/^0x/, "").match(/.{1,2}/g);
|
|
32
|
+
if (!arr)
|
|
33
|
+
return new Uint8Array();
|
|
34
|
+
return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
|
|
35
|
+
};
|
|
36
|
+
var tfhePublicKeyDeserializer = (buff) => {
|
|
37
|
+
TfheCompactPublicKey.deserialize(fromHexString(buff));
|
|
38
|
+
};
|
|
39
|
+
var compactPkeCrsDeserializer = (buff) => {
|
|
40
|
+
CompactPkeCrs.deserialize(fromHexString(buff));
|
|
41
|
+
};
|
|
42
|
+
var zkBuilderAndCrsGenerator = (fhe, crs) => {
|
|
43
|
+
const fhePublicKey = TfheCompactPublicKey.deserialize(fromHexString(fhe));
|
|
44
|
+
const zkBuilder = ProvenCompactCiphertextList.builder(fhePublicKey);
|
|
45
|
+
const zkCrs = CompactPkeCrs.deserialize(fromHexString(crs));
|
|
46
|
+
return { zkBuilder, zkCrs };
|
|
47
|
+
};
|
|
48
|
+
function createCofhesdkConfig(config) {
|
|
49
|
+
return createCofhesdkConfigBase({
|
|
50
|
+
...config,
|
|
51
|
+
fheKeyStorage: config.fheKeyStorage === null ? null : config.fheKeyStorage ?? createWebStorage()
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function createCofhesdkClient(config) {
|
|
55
|
+
return createCofhesdkClientBase({
|
|
56
|
+
config,
|
|
57
|
+
zkBuilderAndCrsGenerator,
|
|
58
|
+
tfhePublicKeyDeserializer,
|
|
59
|
+
compactPkeCrsDeserializer,
|
|
60
|
+
initTfhe
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { createCofhesdkClient, createCofhesdkConfig };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { type CofhesdkClient } from '@/core';
|
|
2
|
+
import { arbSepolia as cofhesdkArbSepolia } from '@/chains';
|
|
3
|
+
|
|
4
|
+
import { describe, it, expect, beforeAll, beforeEach, vi } 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 { createCofhesdkClient, createCofhesdkConfig } from './index.js';
|
|
10
|
+
|
|
11
|
+
// Real test setup - no mocks
|
|
12
|
+
const TEST_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
|
|
13
|
+
const TEST_ACCOUNT = privateKeyToAccount(TEST_PRIVATE_KEY).address;
|
|
14
|
+
|
|
15
|
+
describe('@cofhe/node - Client Integration Tests', () => {
|
|
16
|
+
let cofhesdkClient: CofhesdkClient;
|
|
17
|
+
let publicClient: PublicClient;
|
|
18
|
+
let walletClient: WalletClient;
|
|
19
|
+
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
// Create real viem clients
|
|
22
|
+
publicClient = createPublicClient({
|
|
23
|
+
chain: viemArbitrumSepolia,
|
|
24
|
+
transport: http(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const account = privateKeyToAccount(TEST_PRIVATE_KEY);
|
|
28
|
+
walletClient = createWalletClient({
|
|
29
|
+
chain: viemArbitrumSepolia,
|
|
30
|
+
transport: http(),
|
|
31
|
+
account,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
const config = createCofhesdkConfig({
|
|
37
|
+
supportedChains: [cofhesdkArbSepolia],
|
|
38
|
+
});
|
|
39
|
+
cofhesdkClient = createCofhesdkClient(config);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('Real Client Initialization', () => {
|
|
43
|
+
it('should create a client with real node-tfhe', () => {
|
|
44
|
+
expect(cofhesdkClient).toBeDefined();
|
|
45
|
+
expect(cofhesdkClient.config).toBeDefined();
|
|
46
|
+
expect(cofhesdkClient.connected).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should automatically use filesystem storage as default', () => {
|
|
50
|
+
expect(cofhesdkClient.config.fheKeyStorage).toBeDefined();
|
|
51
|
+
expect(cofhesdkClient.config.fheKeyStorage).not.toBeNull();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should have all expected methods', () => {
|
|
55
|
+
expect(typeof cofhesdkClient.connect).toBe('function');
|
|
56
|
+
expect(typeof cofhesdkClient.encryptInputs).toBe('function');
|
|
57
|
+
expect(typeof cofhesdkClient.decryptHandle).toBe('function');
|
|
58
|
+
expect(typeof cofhesdkClient.getSnapshot).toBe('function');
|
|
59
|
+
expect(typeof cofhesdkClient.subscribe).toBe('function');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('Real Connection', () => {
|
|
64
|
+
it('should connect to real chain', async () => {
|
|
65
|
+
const result = await cofhesdkClient.connect(publicClient, walletClient);
|
|
66
|
+
|
|
67
|
+
expect(result.success).toBe(true);
|
|
68
|
+
expect(cofhesdkClient.connected).toBe(true);
|
|
69
|
+
|
|
70
|
+
const snapshot = cofhesdkClient.getSnapshot();
|
|
71
|
+
expect(snapshot.connected).toBe(true);
|
|
72
|
+
expect(snapshot.chainId).toBe(cofhesdkArbSepolia.id);
|
|
73
|
+
expect(snapshot.account).toBe(TEST_ACCOUNT);
|
|
74
|
+
}, 30000);
|
|
75
|
+
|
|
76
|
+
it('should handle real network errors', async () => {
|
|
77
|
+
const result = await cofhesdkClient.connect(
|
|
78
|
+
{
|
|
79
|
+
getChainId: vi.fn().mockRejectedValue(new Error('Network error')),
|
|
80
|
+
} as unknown as PublicClient,
|
|
81
|
+
walletClient
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
expect(result.success).toBe(false);
|
|
85
|
+
expect(cofhesdkClient.connected).toBe(false);
|
|
86
|
+
}, 30000);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('State Management', () => {
|
|
90
|
+
it('should track connection state changes', async () => {
|
|
91
|
+
const states: any[] = [];
|
|
92
|
+
const unsubscribe = cofhesdkClient.subscribe((snapshot) => {
|
|
93
|
+
states.push({ ...snapshot });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await cofhesdkClient.connect(publicClient, walletClient);
|
|
97
|
+
|
|
98
|
+
unsubscribe();
|
|
99
|
+
|
|
100
|
+
expect(states.length).toBeGreaterThan(0);
|
|
101
|
+
|
|
102
|
+
// First state should be connecting
|
|
103
|
+
const firstState = states.find((s) => s.connecting);
|
|
104
|
+
expect(firstState).toBeDefined();
|
|
105
|
+
expect(firstState?.connecting).toBe(true);
|
|
106
|
+
expect(firstState?.connected).toBe(false);
|
|
107
|
+
|
|
108
|
+
// Last state should be connected
|
|
109
|
+
const lastState = states[states.length - 1];
|
|
110
|
+
expect(lastState.connected).toBe(true);
|
|
111
|
+
expect(lastState.connecting).toBe(false);
|
|
112
|
+
expect(lastState.chainId).toBe(cofhesdkArbSepolia.id);
|
|
113
|
+
}, 30000);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('Initialization Results', () => {
|
|
117
|
+
it('should have keyFetchResult promise', () => {
|
|
118
|
+
expect(cofhesdkClient.initializationResults).toBeDefined();
|
|
119
|
+
expect(cofhesdkClient.initializationResults.keyFetchResult).toBeInstanceOf(Promise);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should resolve keyFetchResult', async () => {
|
|
123
|
+
const result = await cofhesdkClient.initializationResults.keyFetchResult;
|
|
124
|
+
expect(result.success).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('Builder Creation', () => {
|
|
129
|
+
it('should create encrypt builder after connection', async () => {
|
|
130
|
+
await cofhesdkClient.connect(publicClient, walletClient);
|
|
131
|
+
|
|
132
|
+
const builder = cofhesdkClient.encryptInputs([{ data: 100n, utype: 2 }]);
|
|
133
|
+
|
|
134
|
+
expect(builder).toBeDefined();
|
|
135
|
+
expect(typeof builder.setChainId).toBe('function');
|
|
136
|
+
expect(typeof builder.setAccount).toBe('function');
|
|
137
|
+
expect(typeof builder.setSecurityZone).toBe('function');
|
|
138
|
+
expect(typeof builder.encrypt).toBe('function');
|
|
139
|
+
}, 30000);
|
|
140
|
+
|
|
141
|
+
it('should create decrypt builder after connection', async () => {
|
|
142
|
+
await cofhesdkClient.connect(publicClient, walletClient);
|
|
143
|
+
|
|
144
|
+
const builder = cofhesdkClient.decryptHandle(123n, 2);
|
|
145
|
+
|
|
146
|
+
expect(builder).toBeDefined();
|
|
147
|
+
expect(typeof builder.setChainId).toBe('function');
|
|
148
|
+
expect(typeof builder.setAccount).toBe('function');
|
|
149
|
+
expect(typeof builder.decrypt).toBe('function');
|
|
150
|
+
}, 30000);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { arbSepolia } from '@/chains';
|
|
2
|
+
|
|
3
|
+
import { describe, it, expect } from 'vitest';
|
|
4
|
+
import { createCofhesdkConfig, createCofhesdkClient } from './index.js';
|
|
5
|
+
|
|
6
|
+
describe('@cofhe/node - Config', () => {
|
|
7
|
+
describe('createCofhesdkConfig', () => {
|
|
8
|
+
it('should automatically inject filesystem storage as default', () => {
|
|
9
|
+
const config = createCofhesdkConfig({
|
|
10
|
+
supportedChains: [arbSepolia],
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
expect(config.fheKeyStorage).toBeDefined();
|
|
14
|
+
expect(config.fheKeyStorage).not.toBeNull();
|
|
15
|
+
expect(config.supportedChains).toEqual([arbSepolia]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should allow overriding storage', async () => {
|
|
19
|
+
const customStorage = {
|
|
20
|
+
getItem: () => Promise.resolve(10),
|
|
21
|
+
setItem: () => Promise.resolve(),
|
|
22
|
+
removeItem: () => Promise.resolve(),
|
|
23
|
+
};
|
|
24
|
+
const config = createCofhesdkConfig({
|
|
25
|
+
supportedChains: [arbSepolia],
|
|
26
|
+
fheKeyStorage: customStorage,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(await config.fheKeyStorage!.getItem('test')).toBe(10);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should allow null storage', () => {
|
|
33
|
+
const config = createCofhesdkConfig({
|
|
34
|
+
supportedChains: [arbSepolia],
|
|
35
|
+
fheKeyStorage: null,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
expect(config.fheKeyStorage).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should preserve all other config options', () => {
|
|
42
|
+
const config = createCofhesdkConfig({
|
|
43
|
+
supportedChains: [arbSepolia],
|
|
44
|
+
mocks: {
|
|
45
|
+
sealOutputDelay: 0,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(config.supportedChains).toEqual([arbSepolia]);
|
|
50
|
+
expect(config.mocks.sealOutputDelay).toBe(0);
|
|
51
|
+
expect(config.fheKeyStorage).toBeDefined();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('createCofhesdkClient with config', () => {
|
|
56
|
+
it('should create client with validated config', () => {
|
|
57
|
+
const config = createCofhesdkConfig({
|
|
58
|
+
supportedChains: [arbSepolia],
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const client = createCofhesdkClient(config);
|
|
62
|
+
|
|
63
|
+
expect(client).toBeDefined();
|
|
64
|
+
expect(client.config).toBe(config);
|
|
65
|
+
expect(client.config.fheKeyStorage).toBeDefined();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { Encryptable, FheTypes, type CofhesdkClient, type Result, CofhesdkErrorCode, CofhesdkError } from '@/core';
|
|
2
|
+
import { arbSepolia as cofhesdkArbSepolia } 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 { createCofhesdkClient, createCofhesdkConfig } from './index.js';
|
|
10
|
+
|
|
11
|
+
// Real test setup - using actual node-tfhe
|
|
12
|
+
const TEST_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
|
|
13
|
+
|
|
14
|
+
const expectResultSuccess = <T>(result: Result<T>): T => {
|
|
15
|
+
expect(result.success, `Result error: ${result.error?.toString()}`).toBe(true);
|
|
16
|
+
return result.data!;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const expectResultError = <T>(result: Result<T>, errorCode?: CofhesdkErrorCode): void => {
|
|
20
|
+
expect(result.success).toBe(false);
|
|
21
|
+
expect(result.data).toBe(null);
|
|
22
|
+
expect(result.error).not.toBe(null);
|
|
23
|
+
const error = result.error as CofhesdkError;
|
|
24
|
+
expect(error).toBeInstanceOf(CofhesdkError);
|
|
25
|
+
if (errorCode) {
|
|
26
|
+
expect(error.code, `Result error: ${result.error?.toString()}`).toBe(errorCode);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
describe('@cofhe/node - Encrypt Inputs', () => {
|
|
31
|
+
let cofhesdkClient: CofhesdkClient;
|
|
32
|
+
let publicClient: PublicClient;
|
|
33
|
+
let walletClient: WalletClient;
|
|
34
|
+
|
|
35
|
+
beforeAll(() => {
|
|
36
|
+
// Create real viem clients
|
|
37
|
+
publicClient = createPublicClient({
|
|
38
|
+
chain: viemArbitrumSepolia,
|
|
39
|
+
transport: http(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const account = privateKeyToAccount(TEST_PRIVATE_KEY);
|
|
43
|
+
walletClient = createWalletClient({
|
|
44
|
+
chain: viemArbitrumSepolia,
|
|
45
|
+
transport: http(),
|
|
46
|
+
account,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
const config = createCofhesdkConfig({
|
|
52
|
+
supportedChains: [cofhesdkArbSepolia],
|
|
53
|
+
});
|
|
54
|
+
cofhesdkClient = createCofhesdkClient(config);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('Real TFHE Initialization', () => {
|
|
58
|
+
it('should initialize node-tfhe on first encryption', async () => {
|
|
59
|
+
await cofhesdkClient.connect(publicClient, walletClient);
|
|
60
|
+
|
|
61
|
+
// This will trigger real TFHE initialization
|
|
62
|
+
const result = await cofhesdkClient.encryptInputs([Encryptable.uint128(100n)]).encrypt();
|
|
63
|
+
const encrypted = expectResultSuccess(result);
|
|
64
|
+
|
|
65
|
+
// If we get here, TFHE was initialized successfully
|
|
66
|
+
expect(encrypted).toBeDefined();
|
|
67
|
+
}, 60000); // Longer timeout for real operations
|
|
68
|
+
|
|
69
|
+
it('should handle multiple encryptions without re-initializing', async () => {
|
|
70
|
+
await cofhesdkClient.connect(publicClient, walletClient);
|
|
71
|
+
|
|
72
|
+
// First encryption
|
|
73
|
+
const result1 = await cofhesdkClient.encryptInputs([Encryptable.uint128(100n)]).encrypt();
|
|
74
|
+
expectResultSuccess(result1);
|
|
75
|
+
|
|
76
|
+
// Second encryption should reuse initialization
|
|
77
|
+
const result2 = await cofhesdkClient.encryptInputs([Encryptable.uint64(50n)]).encrypt();
|
|
78
|
+
expectResultSuccess(result2);
|
|
79
|
+
}, 120000);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('Real Encryption', () => {
|
|
83
|
+
it('should encrypt a bool with real TFHE', async () => {
|
|
84
|
+
await cofhesdkClient.connect(publicClient, walletClient);
|
|
85
|
+
|
|
86
|
+
const result = await cofhesdkClient.encryptInputs([Encryptable.bool(true)]).encrypt();
|
|
87
|
+
const encrypted = expectResultSuccess(result);
|
|
88
|
+
|
|
89
|
+
expect(encrypted.length).toBe(1);
|
|
90
|
+
expect(encrypted[0].utype).toBe(FheTypes.Bool);
|
|
91
|
+
expect(encrypted[0].ctHash).toBeDefined();
|
|
92
|
+
expect(typeof encrypted[0].ctHash).toBe('bigint');
|
|
93
|
+
expect(encrypted[0].signature).toBeDefined();
|
|
94
|
+
expect(typeof encrypted[0].signature).toBe('string');
|
|
95
|
+
expect(encrypted[0].securityZone).toBe(0);
|
|
96
|
+
}, 60000);
|
|
97
|
+
|
|
98
|
+
it('should encrypt all supported types together', async () => {
|
|
99
|
+
await cofhesdkClient.connect(publicClient, walletClient);
|
|
100
|
+
|
|
101
|
+
const inputs = [
|
|
102
|
+
Encryptable.bool(false),
|
|
103
|
+
Encryptable.uint8(1n),
|
|
104
|
+
Encryptable.uint16(2n),
|
|
105
|
+
Encryptable.uint32(3n),
|
|
106
|
+
Encryptable.uint64(4n),
|
|
107
|
+
Encryptable.uint128(5n),
|
|
108
|
+
Encryptable.address('0x742d35Cc6634C0532925a3b844D16faC4c175E99'),
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
const result = await cofhesdkClient.encryptInputs(inputs).encrypt();
|
|
112
|
+
const encrypted = expectResultSuccess(result);
|
|
113
|
+
|
|
114
|
+
expect(encrypted.length).toBe(7);
|
|
115
|
+
// Verify each type
|
|
116
|
+
expect(encrypted[0].utype).toBe(FheTypes.Bool);
|
|
117
|
+
expect(encrypted[1].utype).toBe(FheTypes.Uint8);
|
|
118
|
+
expect(encrypted[2].utype).toBe(FheTypes.Uint16);
|
|
119
|
+
expect(encrypted[3].utype).toBe(FheTypes.Uint32);
|
|
120
|
+
expect(encrypted[4].utype).toBe(FheTypes.Uint64);
|
|
121
|
+
expect(encrypted[5].utype).toBe(FheTypes.Uint128);
|
|
122
|
+
expect(encrypted[6].utype).toBe(FheTypes.Uint160);
|
|
123
|
+
}, 90000); // Longer timeout for multiple encryptions
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('Real Builder Pattern', () => {
|
|
127
|
+
it('should support chaining builder methods with real encryption', async () => {
|
|
128
|
+
await cofhesdkClient.connect(publicClient, walletClient);
|
|
129
|
+
|
|
130
|
+
const snapshot = cofhesdkClient.getSnapshot();
|
|
131
|
+
const result = await cofhesdkClient
|
|
132
|
+
.encryptInputs([Encryptable.uint128(100n)])
|
|
133
|
+
.setChainId(snapshot.chainId!)
|
|
134
|
+
.setAccount(snapshot.account!)
|
|
135
|
+
.setSecurityZone(0)
|
|
136
|
+
.encrypt();
|
|
137
|
+
|
|
138
|
+
const encrypted = expectResultSuccess(result);
|
|
139
|
+
expect(encrypted.length).toBe(1);
|
|
140
|
+
expect(encrypted[0].utype).toBe(FheTypes.Uint128);
|
|
141
|
+
}, 60000);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('Real Error Handling', () => {
|
|
145
|
+
it('should fail gracefully when not connected', async () => {
|
|
146
|
+
// Don't connect the client
|
|
147
|
+
const result = await cofhesdkClient.encryptInputs([Encryptable.uint128(100n)]).encrypt();
|
|
148
|
+
|
|
149
|
+
expectResultError(result, CofhesdkErrorCode.NotConnected);
|
|
150
|
+
}, 30000);
|
|
151
|
+
|
|
152
|
+
it('should handle invalid CoFHE URL', async () => {
|
|
153
|
+
const badConfig = createCofhesdkConfig({
|
|
154
|
+
supportedChains: [
|
|
155
|
+
{
|
|
156
|
+
...cofhesdkArbSepolia,
|
|
157
|
+
coFheUrl: 'http://invalid-cofhe-url.local',
|
|
158
|
+
verifierUrl: 'http://invalid-verifier-url.local',
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
fheKeysPrefetching: 'OFF',
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const badClient = createCofhesdkClient(badConfig);
|
|
165
|
+
await badClient.connect(publicClient, walletClient);
|
|
166
|
+
|
|
167
|
+
const result = await badClient.encryptInputs([Encryptable.uint128(100n)]).encrypt();
|
|
168
|
+
|
|
169
|
+
expect(result.success).toBe(false);
|
|
170
|
+
if (!result.success) {
|
|
171
|
+
expect(result.error).toBeDefined();
|
|
172
|
+
}
|
|
173
|
+
}, 60000);
|
|
174
|
+
});
|
|
175
|
+
});
|
package/node/index.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Node.js specific functionality only
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createCofhesdkClientBase,
|
|
5
|
+
createCofhesdkConfigBase,
|
|
6
|
+
type CofhesdkClient,
|
|
7
|
+
type CofhesdkConfig,
|
|
8
|
+
type CofhesdkInputConfig,
|
|
9
|
+
type ZkBuilderAndCrsGenerator,
|
|
10
|
+
type FheKeyDeserializer,
|
|
11
|
+
} from '@/core';
|
|
12
|
+
|
|
13
|
+
// Import node-specific storage (internal use only)
|
|
14
|
+
import { createNodeStorage } from './storage.js';
|
|
15
|
+
|
|
16
|
+
// Import node-tfhe for Node.js
|
|
17
|
+
import { TfheCompactPublicKey, ProvenCompactCiphertextList, CompactPkeCrs, init_panic_hook } from 'node-tfhe';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Internal function to initialize TFHE for Node.js
|
|
21
|
+
* Called automatically on first encryption - users don't need to call this manually
|
|
22
|
+
* @returns true if TFHE was initialized, false if already initialized
|
|
23
|
+
*/
|
|
24
|
+
let tfheInitialized = false;
|
|
25
|
+
async function initTfhe(): Promise<boolean> {
|
|
26
|
+
if (tfheInitialized) return false;
|
|
27
|
+
await init_panic_hook();
|
|
28
|
+
tfheInitialized = true;
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Utility to convert the hex string key to a Uint8Array for use with tfhe
|
|
34
|
+
*/
|
|
35
|
+
const fromHexString = (hexString: string): Uint8Array => {
|
|
36
|
+
const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
|
|
37
|
+
const arr = cleanString.replace(/^0x/, '').match(/.{1,2}/g);
|
|
38
|
+
if (!arr) return new Uint8Array();
|
|
39
|
+
return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Serializer for TFHE public keys
|
|
44
|
+
* Validates that the buffer can be deserialized into a TfheCompactPublicKey
|
|
45
|
+
*/
|
|
46
|
+
const tfhePublicKeyDeserializer: FheKeyDeserializer = (buff: string): void => {
|
|
47
|
+
TfheCompactPublicKey.deserialize(fromHexString(buff));
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Serializer for Compact PKE CRS
|
|
52
|
+
* Validates that the buffer can be deserialized into ZkCompactPkePublicParams
|
|
53
|
+
*/
|
|
54
|
+
const compactPkeCrsDeserializer: FheKeyDeserializer = (buff: string): void => {
|
|
55
|
+
CompactPkeCrs.deserialize(fromHexString(buff));
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a ZK builder and CRS from FHE public key and CRS buffers
|
|
60
|
+
* This is used internally by the SDK to create encrypted inputs
|
|
61
|
+
*/
|
|
62
|
+
const zkBuilderAndCrsGenerator: ZkBuilderAndCrsGenerator = (fhe: string, crs: string) => {
|
|
63
|
+
const fhePublicKey = TfheCompactPublicKey.deserialize(fromHexString(fhe));
|
|
64
|
+
const zkBuilder = ProvenCompactCiphertextList.builder(fhePublicKey);
|
|
65
|
+
const zkCrs = CompactPkeCrs.deserialize(fromHexString(crs));
|
|
66
|
+
|
|
67
|
+
return { zkBuilder, zkCrs };
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Creates a CoFHE SDK configuration for Node.js with filesystem storage as default
|
|
72
|
+
* @param config - The CoFHE SDK input configuration (fheKeyStorage will default to filesystem if not provided)
|
|
73
|
+
* @returns The CoFHE SDK configuration with Node.js defaults applied
|
|
74
|
+
*/
|
|
75
|
+
export function createCofhesdkConfig(config: CofhesdkInputConfig): CofhesdkConfig {
|
|
76
|
+
return createCofhesdkConfigBase({
|
|
77
|
+
...config,
|
|
78
|
+
fheKeyStorage: config.fheKeyStorage === null ? null : config.fheKeyStorage ?? createNodeStorage(),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Creates a CoFHE SDK client instance for Node.js with node-tfhe automatically configured
|
|
84
|
+
* TFHE will be initialized automatically on first encryption - no manual setup required
|
|
85
|
+
* @param config - The CoFHE SDK configuration (use createCofhesdkConfig to create with Node.js defaults)
|
|
86
|
+
* @returns The CoFHE SDK client instance
|
|
87
|
+
*/
|
|
88
|
+
export function createCofhesdkClient(config: CofhesdkConfig): CofhesdkClient {
|
|
89
|
+
return createCofhesdkClientBase({
|
|
90
|
+
config,
|
|
91
|
+
zkBuilderAndCrsGenerator,
|
|
92
|
+
tfhePublicKeyDeserializer,
|
|
93
|
+
compactPkeCrsDeserializer,
|
|
94
|
+
initTfhe,
|
|
95
|
+
});
|
|
96
|
+
}
|
package/node/storage.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/* eslint-disable turbo/no-undeclared-env-vars */
|
|
2
|
+
|
|
3
|
+
import type { IStorage } from '@/core';
|
|
4
|
+
|
|
5
|
+
import { promises as fs } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
|
|
8
|
+
// Memory storage fallback
|
|
9
|
+
const memoryStorage: Record<string, string> = {};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Creates a node storage implementation using the filesystem
|
|
13
|
+
* @returns IStorage implementation for Node.js environments
|
|
14
|
+
*/
|
|
15
|
+
export const createNodeStorage = (): IStorage => {
|
|
16
|
+
return {
|
|
17
|
+
getItem: async (name: string) => {
|
|
18
|
+
try {
|
|
19
|
+
const storageDir = join(process.env.HOME || process.env.USERPROFILE || '.', '.cofhesdk');
|
|
20
|
+
await fs.mkdir(storageDir, { recursive: true });
|
|
21
|
+
const filePath = join(storageDir, `${name}.json`);
|
|
22
|
+
const data = await fs.readFile(filePath, 'utf8').catch(() => null);
|
|
23
|
+
return data ? JSON.parse(data) : null;
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.warn('Node.js filesystem modules not available, falling back to memory storage' + e);
|
|
26
|
+
return memoryStorage[name] || null;
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
setItem: async (name: string, value: any) => {
|
|
30
|
+
try {
|
|
31
|
+
const storageDir = join(process.env.HOME || process.env.USERPROFILE || '.', '.cofhesdk');
|
|
32
|
+
await fs.mkdir(storageDir, { recursive: true });
|
|
33
|
+
const filePath = join(storageDir, `${name}.json`);
|
|
34
|
+
await fs.writeFile(filePath, JSON.stringify(value));
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.warn('Node.js filesystem modules not available, falling back to memory storage' + e);
|
|
37
|
+
memoryStorage[name] = JSON.stringify(value);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
removeItem: async (name: string) => {
|
|
41
|
+
try {
|
|
42
|
+
const storageDir = join(process.env.HOME || process.env.USERPROFILE || '.', '.cofhesdk');
|
|
43
|
+
const filePath = join(storageDir, `${name}.json`);
|
|
44
|
+
await fs.unlink(filePath).catch(() => {});
|
|
45
|
+
} catch (e) {
|
|
46
|
+
console.warn('Node.js filesystem modules not available, falling back to memory storage' + e);
|
|
47
|
+
delete memoryStorage[name];
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
};
|