@cofhe/sdk 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/adapters/ethers5.test.ts +174 -0
- package/adapters/ethers5.ts +36 -0
- package/adapters/ethers6.test.ts +169 -0
- package/adapters/ethers6.ts +36 -0
- package/adapters/hardhat-node.ts +167 -0
- package/adapters/hardhat.hh2.test.ts +159 -0
- package/adapters/hardhat.ts +36 -0
- package/adapters/index.test.ts +20 -0
- package/adapters/index.ts +5 -0
- package/adapters/smartWallet.ts +99 -0
- package/adapters/test-utils.ts +53 -0
- package/adapters/types.ts +6 -0
- package/adapters/wagmi.test.ts +156 -0
- package/adapters/wagmi.ts +17 -0
- package/chains/chains/arbSepolia.ts +14 -0
- package/chains/chains/baseSepolia.ts +14 -0
- package/chains/chains/hardhat.ts +15 -0
- package/chains/chains/localcofhe.ts +14 -0
- package/chains/chains/sepolia.ts +14 -0
- package/chains/chains.test.ts +50 -0
- package/chains/defineChain.ts +18 -0
- package/chains/index.ts +35 -0
- package/chains/types.ts +32 -0
- package/core/baseBuilder.ts +119 -0
- package/core/client.test.ts +315 -0
- package/core/client.ts +292 -0
- package/core/clientTypes.ts +108 -0
- package/core/config.test.ts +235 -0
- package/core/config.ts +220 -0
- package/core/decrypt/MockQueryDecrypterAbi.ts +129 -0
- package/core/decrypt/cofheMocksSealOutput.ts +57 -0
- package/core/decrypt/decryptHandleBuilder.ts +287 -0
- package/core/decrypt/decryptUtils.ts +28 -0
- package/core/decrypt/tnSealOutputV1.ts +59 -0
- package/core/decrypt/tnSealOutputV2.ts +298 -0
- package/core/encrypt/MockZkVerifierAbi.ts +106 -0
- package/core/encrypt/cofheMocksZkVerifySign.ts +284 -0
- package/core/encrypt/encryptInputsBuilder.test.ts +751 -0
- package/core/encrypt/encryptInputsBuilder.ts +560 -0
- package/core/encrypt/encryptUtils.ts +67 -0
- package/core/encrypt/zkPackProveVerify.ts +335 -0
- package/core/error.ts +168 -0
- package/core/fetchKeys.test.ts +195 -0
- package/core/fetchKeys.ts +144 -0
- package/core/index.ts +89 -0
- package/core/keyStore.test.ts +226 -0
- package/core/keyStore.ts +154 -0
- package/core/permits.test.ts +494 -0
- package/core/permits.ts +200 -0
- package/core/types.ts +398 -0
- package/core/utils.ts +130 -0
- package/dist/adapters.cjs +88 -0
- package/dist/adapters.d.cts +14576 -0
- package/dist/adapters.d.ts +14576 -0
- package/dist/adapters.js +83 -0
- package/dist/chains.cjs +114 -0
- package/dist/chains.d.cts +121 -0
- package/dist/chains.d.ts +121 -0
- package/dist/chains.js +1 -0
- package/dist/chunk-UGBVZNRT.js +818 -0
- package/dist/chunk-WEAZ25JO.js +105 -0
- package/dist/chunk-WGCRJCBR.js +2523 -0
- package/dist/clientTypes-5_1nwtUe.d.cts +914 -0
- package/dist/clientTypes-Es7fyi65.d.ts +914 -0
- package/dist/core.cjs +3414 -0
- package/dist/core.d.cts +111 -0
- package/dist/core.d.ts +111 -0
- package/dist/core.js +3 -0
- package/dist/node.cjs +3286 -0
- package/dist/node.d.cts +22 -0
- package/dist/node.d.ts +22 -0
- package/dist/node.js +91 -0
- package/dist/permit-fUSe6KKq.d.cts +349 -0
- package/dist/permit-fUSe6KKq.d.ts +349 -0
- package/dist/permits.cjs +871 -0
- package/dist/permits.d.cts +1045 -0
- package/dist/permits.d.ts +1045 -0
- package/dist/permits.js +1 -0
- package/dist/types-KImPrEIe.d.cts +48 -0
- package/dist/types-KImPrEIe.d.ts +48 -0
- package/dist/web.cjs +3478 -0
- package/dist/web.d.cts +38 -0
- package/dist/web.d.ts +38 -0
- package/dist/web.js +240 -0
- package/dist/zkProve.worker.cjs +93 -0
- package/dist/zkProve.worker.d.cts +2 -0
- package/dist/zkProve.worker.d.ts +2 -0
- package/dist/zkProve.worker.js +91 -0
- package/node/client.test.ts +147 -0
- package/node/config.test.ts +68 -0
- package/node/encryptInputs.test.ts +155 -0
- package/node/index.ts +97 -0
- package/node/storage.ts +51 -0
- package/package.json +27 -15
- package/permits/index.ts +68 -0
- package/permits/localstorage.test.ts +117 -0
- package/permits/permit.test.ts +477 -0
- package/permits/permit.ts +405 -0
- package/permits/sealing.test.ts +84 -0
- package/permits/sealing.ts +131 -0
- package/permits/signature.ts +79 -0
- package/permits/store.test.ts +128 -0
- package/permits/store.ts +166 -0
- package/permits/test-utils.ts +20 -0
- package/permits/types.ts +191 -0
- package/permits/utils.ts +62 -0
- package/permits/validation.test.ts +288 -0
- package/permits/validation.ts +369 -0
- package/web/client.web.test.ts +147 -0
- package/web/config.web.test.ts +69 -0
- package/web/encryptInputs.web.test.ts +172 -0
- package/web/index.ts +161 -0
- package/web/storage.ts +34 -0
- package/web/worker.builder.web.test.ts +148 -0
- package/web/worker.config.web.test.ts +329 -0
- package/web/worker.output.web.test.ts +84 -0
- package/web/workerManager.test.ts +80 -0
- package/web/workerManager.ts +214 -0
- package/web/workerManager.web.test.ts +114 -0
- package/web/zkProve.worker.ts +133 -0
package/core/config.ts
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { type CofheChain } from '@/chains';
|
|
2
|
+
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { type WalletClient } from 'viem';
|
|
5
|
+
import { CofhesdkError, CofhesdkErrorCode } from './error.js';
|
|
6
|
+
import { type IStorage } from './types.js';
|
|
7
|
+
|
|
8
|
+
export type CofhesdkEnvironment = 'node' | 'hardhat' | 'web' | 'react';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Usable config type inferred from the schema
|
|
12
|
+
*/
|
|
13
|
+
export type CofhesdkConfig = {
|
|
14
|
+
/** Environment that the SDK is running in */
|
|
15
|
+
environment: 'node' | 'hardhat' | 'web' | 'react';
|
|
16
|
+
/** List of supported chains */
|
|
17
|
+
supportedChains: CofheChain[];
|
|
18
|
+
/**
|
|
19
|
+
* How permits are generated
|
|
20
|
+
* - ON_CONNECT: Generate a permit when client.connect() is called
|
|
21
|
+
* - ON_DECRYPT_HANDLES: Generate a permit when client.decryptHandles() is called
|
|
22
|
+
* - MANUAL: Generate a permit manually using client.generatePermit()
|
|
23
|
+
*/
|
|
24
|
+
permitGeneration: 'ON_CONNECT' | 'ON_DECRYPT_HANDLES' | 'MANUAL';
|
|
25
|
+
/** Default permit expiration in seconds, default is 30 days */
|
|
26
|
+
defaultPermitExpiration: number;
|
|
27
|
+
/**
|
|
28
|
+
* Storage scheme for the fetched fhe keys
|
|
29
|
+
* FHE keys are large, and caching prevents re-fetching them on each encryptInputs call
|
|
30
|
+
* (defaults to indexedDB on web, filesystem on node)
|
|
31
|
+
*/
|
|
32
|
+
fheKeyStorage: IStorage | null;
|
|
33
|
+
/**
|
|
34
|
+
* Whether to use Web Workers for ZK proof generation (web platform only)
|
|
35
|
+
* When enabled, heavy WASM computation is offloaded to prevent UI freezing
|
|
36
|
+
* Default: true
|
|
37
|
+
*/
|
|
38
|
+
useWorkers: boolean;
|
|
39
|
+
/** Mocks configs */
|
|
40
|
+
mocks: {
|
|
41
|
+
/**
|
|
42
|
+
* Length of the simulated seal output delay in milliseconds
|
|
43
|
+
* Default 1000ms on web
|
|
44
|
+
* Default 0ms on hardhat (will be called during tests no need for fake delay)
|
|
45
|
+
*/
|
|
46
|
+
sealOutputDelay: number;
|
|
47
|
+
};
|
|
48
|
+
_internal?: CofhesdkInternalConfig;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type CofhesdkInternalConfig = {
|
|
52
|
+
zkvWalletClient?: WalletClient;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Zod schema for configuration validation
|
|
57
|
+
*/
|
|
58
|
+
export const CofhesdkConfigSchema = z.object({
|
|
59
|
+
/** Environment that the SDK is running in */
|
|
60
|
+
environment: z.enum(['node', 'hardhat', 'web', 'react']).optional().default('node'),
|
|
61
|
+
/** List of supported chain configurations */
|
|
62
|
+
supportedChains: z.array(z.custom<CofheChain>()),
|
|
63
|
+
/** How permits are generated */
|
|
64
|
+
permitGeneration: z.enum(['ON_CONNECT', 'ON_DECRYPT_HANDLES', 'MANUAL']).optional().default('ON_CONNECT'),
|
|
65
|
+
/** Default permit expiration in seconds, default is 30 days */
|
|
66
|
+
defaultPermitExpiration: z
|
|
67
|
+
.number()
|
|
68
|
+
.optional()
|
|
69
|
+
.default(60 * 60 * 24 * 30),
|
|
70
|
+
/** Storage method for fhe keys (defaults to indexedDB on web, filesystem on node) */
|
|
71
|
+
fheKeyStorage: z
|
|
72
|
+
.object({
|
|
73
|
+
getItem: z.function().args(z.string()).returns(z.promise(z.any())),
|
|
74
|
+
setItem: z.function().args(z.string(), z.any()).returns(z.promise(z.void())),
|
|
75
|
+
removeItem: z.function().args(z.string()).returns(z.promise(z.void())),
|
|
76
|
+
})
|
|
77
|
+
.or(z.null())
|
|
78
|
+
.default(null),
|
|
79
|
+
/** Whether to use Web Workers for ZK proof generation (web platform only) */
|
|
80
|
+
useWorkers: z.boolean().optional().default(true),
|
|
81
|
+
/** Mocks configs */
|
|
82
|
+
mocks: z
|
|
83
|
+
.object({
|
|
84
|
+
sealOutputDelay: z.number().optional().default(0),
|
|
85
|
+
})
|
|
86
|
+
.optional()
|
|
87
|
+
.default({ sealOutputDelay: 0 }),
|
|
88
|
+
/** Internal configuration */
|
|
89
|
+
_internal: z
|
|
90
|
+
.object({
|
|
91
|
+
zkvWalletClient: z.any().optional(),
|
|
92
|
+
})
|
|
93
|
+
.optional(),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Input config type inferred from the schema
|
|
98
|
+
*/
|
|
99
|
+
export type CofhesdkInputConfig = z.input<typeof CofhesdkConfigSchema>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Creates and validates a cofhesdk configuration (base implementation)
|
|
103
|
+
* @param config - The configuration object to validate
|
|
104
|
+
* @returns The validated configuration
|
|
105
|
+
* @throws {Error} If the configuration is invalid
|
|
106
|
+
*/
|
|
107
|
+
export function createCofhesdkConfigBase(config: CofhesdkInputConfig): CofhesdkConfig {
|
|
108
|
+
const result = CofhesdkConfigSchema.safeParse(config);
|
|
109
|
+
|
|
110
|
+
if (!result.success) {
|
|
111
|
+
throw new Error(`Invalid cofhesdk configuration: ${result.error.message}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return result.data;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Access the CofhesdkConfig object directly by providing the key.
|
|
119
|
+
* This is powerful when you use OnchainKit utilities outside of the React context.
|
|
120
|
+
*/
|
|
121
|
+
export const getCofhesdkConfigItem = <K extends keyof CofhesdkConfig>(
|
|
122
|
+
config: CofhesdkConfig,
|
|
123
|
+
key: K
|
|
124
|
+
): CofhesdkConfig[K] => {
|
|
125
|
+
return config[key];
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Gets a supported chain from config by chainId, throws if not found
|
|
130
|
+
* @param config - The cofhesdk configuration
|
|
131
|
+
* @param chainId - The chain ID to look up
|
|
132
|
+
* @returns The supported chain configuration
|
|
133
|
+
* @throws {CofhesdkError} If the chain is not found in the config
|
|
134
|
+
*/
|
|
135
|
+
export function getSupportedChainOrThrow(config: CofhesdkConfig, chainId: number): CofheChain {
|
|
136
|
+
const supportedChain = config.supportedChains.find((chain) => chain.id === chainId);
|
|
137
|
+
|
|
138
|
+
if (!supportedChain) {
|
|
139
|
+
throw new CofhesdkError({
|
|
140
|
+
code: CofhesdkErrorCode.UnsupportedChain,
|
|
141
|
+
message: `Config does not support chain <${chainId}>`,
|
|
142
|
+
hint: 'Ensure config passed to client has been created with this chain in the config.supportedChains array.',
|
|
143
|
+
context: {
|
|
144
|
+
chainId,
|
|
145
|
+
supportedChainIds: config.supportedChains.map((c) => c.id),
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return supportedChain;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Gets the CoFHE URL for a chain, throws if not found
|
|
155
|
+
* @param config - The cofhesdk configuration
|
|
156
|
+
* @param chainId - The chain ID to look up
|
|
157
|
+
* @returns The CoFHE URL for the chain
|
|
158
|
+
* @throws {CofhesdkError} If the chain or URL is not found
|
|
159
|
+
*/
|
|
160
|
+
export function getCoFheUrlOrThrow(config: CofhesdkConfig, chainId: number): string {
|
|
161
|
+
const supportedChain = getSupportedChainOrThrow(config, chainId);
|
|
162
|
+
const url = supportedChain.coFheUrl;
|
|
163
|
+
|
|
164
|
+
if (!url) {
|
|
165
|
+
throw new CofhesdkError({
|
|
166
|
+
code: CofhesdkErrorCode.MissingConfig,
|
|
167
|
+
message: `CoFHE URL is not configured for chain <${chainId}>`,
|
|
168
|
+
hint: 'Ensure this chain config includes a coFheUrl property.',
|
|
169
|
+
context: { chainId },
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return url;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Gets the ZK verifier URL for a chain, throws if not found
|
|
178
|
+
* @param config - The cofhesdk configuration
|
|
179
|
+
* @param chainId - The chain ID to look up
|
|
180
|
+
* @returns The ZK verifier URL for the chain
|
|
181
|
+
* @throws {CofhesdkError} If the chain or URL is not found
|
|
182
|
+
*/
|
|
183
|
+
export function getZkVerifierUrlOrThrow(config: CofhesdkConfig, chainId: number): string {
|
|
184
|
+
const supportedChain = getSupportedChainOrThrow(config, chainId);
|
|
185
|
+
const url = supportedChain.verifierUrl;
|
|
186
|
+
|
|
187
|
+
if (!url) {
|
|
188
|
+
throw new CofhesdkError({
|
|
189
|
+
code: CofhesdkErrorCode.ZkVerifierUrlUninitialized,
|
|
190
|
+
message: `ZK verifier URL is not configured for chain <${chainId}>`,
|
|
191
|
+
hint: 'Ensure this chain config includes a verifierUrl property.',
|
|
192
|
+
context: { chainId },
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return url;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Gets the threshold network URL for a chain, throws if not found
|
|
201
|
+
* @param config - The cofhesdk configuration
|
|
202
|
+
* @param chainId - The chain ID to look up
|
|
203
|
+
* @returns The threshold network URL for the chain
|
|
204
|
+
* @throws {CofhesdkError} If the chain or URL is not found
|
|
205
|
+
*/
|
|
206
|
+
export function getThresholdNetworkUrlOrThrow(config: CofhesdkConfig, chainId: number): string {
|
|
207
|
+
const supportedChain = getSupportedChainOrThrow(config, chainId);
|
|
208
|
+
const url = supportedChain.thresholdNetworkUrl;
|
|
209
|
+
|
|
210
|
+
if (!url) {
|
|
211
|
+
throw new CofhesdkError({
|
|
212
|
+
code: CofhesdkErrorCode.ThresholdNetworkUrlUninitialized,
|
|
213
|
+
message: `Threshold network URL is not configured for chain <${chainId}>`,
|
|
214
|
+
hint: 'Ensure this chain config includes a thresholdNetworkUrl property.',
|
|
215
|
+
context: { chainId },
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return url;
|
|
220
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
export const MockQueryDecrypterAbi = [
|
|
2
|
+
{
|
|
3
|
+
type: 'function',
|
|
4
|
+
name: 'acl',
|
|
5
|
+
inputs: [],
|
|
6
|
+
outputs: [{ name: '', type: 'address', internalType: 'contract ACL' }],
|
|
7
|
+
stateMutability: 'view',
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
type: 'function',
|
|
11
|
+
name: 'decodeLowLevelReversion',
|
|
12
|
+
inputs: [{ name: 'data', type: 'bytes', internalType: 'bytes' }],
|
|
13
|
+
outputs: [{ name: 'error', type: 'string', internalType: 'string' }],
|
|
14
|
+
stateMutability: 'pure',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
type: 'function',
|
|
18
|
+
name: 'exists',
|
|
19
|
+
inputs: [],
|
|
20
|
+
outputs: [{ name: '', type: 'bool', internalType: 'bool' }],
|
|
21
|
+
stateMutability: 'pure',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: 'function',
|
|
25
|
+
name: 'initialize',
|
|
26
|
+
inputs: [
|
|
27
|
+
{ name: '_taskManager', type: 'address', internalType: 'address' },
|
|
28
|
+
{ name: '_acl', type: 'address', internalType: 'address' },
|
|
29
|
+
],
|
|
30
|
+
outputs: [],
|
|
31
|
+
stateMutability: 'nonpayable',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
type: 'function',
|
|
35
|
+
name: 'queryDecrypt',
|
|
36
|
+
inputs: [
|
|
37
|
+
{ name: 'ctHash', type: 'uint256', internalType: 'uint256' },
|
|
38
|
+
{ name: '', type: 'uint256', internalType: 'uint256' },
|
|
39
|
+
{
|
|
40
|
+
name: 'permission',
|
|
41
|
+
type: 'tuple',
|
|
42
|
+
internalType: 'struct Permission',
|
|
43
|
+
components: [
|
|
44
|
+
{ name: 'issuer', type: 'address', internalType: 'address' },
|
|
45
|
+
{ name: 'expiration', type: 'uint64', internalType: 'uint64' },
|
|
46
|
+
{ name: 'recipient', type: 'address', internalType: 'address' },
|
|
47
|
+
{ name: 'validatorId', type: 'uint256', internalType: 'uint256' },
|
|
48
|
+
{
|
|
49
|
+
name: 'validatorContract',
|
|
50
|
+
type: 'address',
|
|
51
|
+
internalType: 'address',
|
|
52
|
+
},
|
|
53
|
+
{ name: 'sealingKey', type: 'bytes32', internalType: 'bytes32' },
|
|
54
|
+
{ name: 'issuerSignature', type: 'bytes', internalType: 'bytes' },
|
|
55
|
+
{ name: 'recipientSignature', type: 'bytes', internalType: 'bytes' },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
outputs: [
|
|
60
|
+
{ name: 'allowed', type: 'bool', internalType: 'bool' },
|
|
61
|
+
{ name: 'error', type: 'string', internalType: 'string' },
|
|
62
|
+
{ name: '', type: 'uint256', internalType: 'uint256' },
|
|
63
|
+
],
|
|
64
|
+
stateMutability: 'view',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: 'function',
|
|
68
|
+
name: 'querySealOutput',
|
|
69
|
+
inputs: [
|
|
70
|
+
{ name: 'ctHash', type: 'uint256', internalType: 'uint256' },
|
|
71
|
+
{ name: '', type: 'uint256', internalType: 'uint256' },
|
|
72
|
+
{
|
|
73
|
+
name: 'permission',
|
|
74
|
+
type: 'tuple',
|
|
75
|
+
internalType: 'struct Permission',
|
|
76
|
+
components: [
|
|
77
|
+
{ name: 'issuer', type: 'address', internalType: 'address' },
|
|
78
|
+
{ name: 'expiration', type: 'uint64', internalType: 'uint64' },
|
|
79
|
+
{ name: 'recipient', type: 'address', internalType: 'address' },
|
|
80
|
+
{ name: 'validatorId', type: 'uint256', internalType: 'uint256' },
|
|
81
|
+
{
|
|
82
|
+
name: 'validatorContract',
|
|
83
|
+
type: 'address',
|
|
84
|
+
internalType: 'address',
|
|
85
|
+
},
|
|
86
|
+
{ name: 'sealingKey', type: 'bytes32', internalType: 'bytes32' },
|
|
87
|
+
{ name: 'issuerSignature', type: 'bytes', internalType: 'bytes' },
|
|
88
|
+
{ name: 'recipientSignature', type: 'bytes', internalType: 'bytes' },
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
outputs: [
|
|
93
|
+
{ name: 'allowed', type: 'bool', internalType: 'bool' },
|
|
94
|
+
{ name: 'error', type: 'string', internalType: 'string' },
|
|
95
|
+
{ name: '', type: 'bytes32', internalType: 'bytes32' },
|
|
96
|
+
],
|
|
97
|
+
stateMutability: 'view',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: 'function',
|
|
101
|
+
name: 'seal',
|
|
102
|
+
inputs: [
|
|
103
|
+
{ name: 'input', type: 'uint256', internalType: 'uint256' },
|
|
104
|
+
{ name: 'key', type: 'bytes32', internalType: 'bytes32' },
|
|
105
|
+
],
|
|
106
|
+
outputs: [{ name: '', type: 'bytes32', internalType: 'bytes32' }],
|
|
107
|
+
stateMutability: 'pure',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
type: 'function',
|
|
111
|
+
name: 'taskManager',
|
|
112
|
+
inputs: [],
|
|
113
|
+
outputs: [{ name: '', type: 'address', internalType: 'contract TaskManager' }],
|
|
114
|
+
stateMutability: 'view',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
type: 'function',
|
|
118
|
+
name: 'unseal',
|
|
119
|
+
inputs: [
|
|
120
|
+
{ name: 'hashed', type: 'bytes32', internalType: 'bytes32' },
|
|
121
|
+
{ name: 'key', type: 'bytes32', internalType: 'bytes32' },
|
|
122
|
+
],
|
|
123
|
+
outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }],
|
|
124
|
+
stateMutability: 'pure',
|
|
125
|
+
},
|
|
126
|
+
{ type: 'error', name: 'NotAllowed', inputs: [] },
|
|
127
|
+
{ type: 'error', name: 'SealingKeyInvalid', inputs: [] },
|
|
128
|
+
{ type: 'error', name: 'SealingKeyMissing', inputs: [] },
|
|
129
|
+
] as const;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type Permit, PermitUtils } from '@/permits';
|
|
2
|
+
|
|
3
|
+
import { type PublicClient } from 'viem';
|
|
4
|
+
import { sleep } from '../utils.js';
|
|
5
|
+
import { MockQueryDecrypterAbi } from './MockQueryDecrypterAbi.js';
|
|
6
|
+
import { FheTypes } from '../types.js';
|
|
7
|
+
import { CofhesdkError, CofhesdkErrorCode } from '../error.js';
|
|
8
|
+
|
|
9
|
+
// Address the Mock Query Decrypter contract is deployed to on the Hardhat chain
|
|
10
|
+
export const MockQueryDecrypterAddress = '0x0000000000000000000000000000000000000200';
|
|
11
|
+
|
|
12
|
+
export async function cofheMocksSealOutput(
|
|
13
|
+
ctHash: bigint,
|
|
14
|
+
utype: FheTypes,
|
|
15
|
+
permit: Permit,
|
|
16
|
+
publicClient: PublicClient,
|
|
17
|
+
mocksSealOutputDelay: number
|
|
18
|
+
): Promise<bigint> {
|
|
19
|
+
// Configurable delay before decrypting the output to simulate the CoFHE decrypt processing time
|
|
20
|
+
// Recommended 1000ms on web
|
|
21
|
+
// Recommended 0ms on hardhat (will be called during tests no need for fake delay)
|
|
22
|
+
if (mocksSealOutputDelay > 0) await sleep(mocksSealOutputDelay);
|
|
23
|
+
|
|
24
|
+
const permission = PermitUtils.getPermission(permit, true);
|
|
25
|
+
const permissionWithBigInts = {
|
|
26
|
+
...permission,
|
|
27
|
+
expiration: BigInt(permission.expiration),
|
|
28
|
+
validatorId: BigInt(permission.validatorId),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const [allowed, error, result] = await publicClient.readContract({
|
|
32
|
+
address: MockQueryDecrypterAddress,
|
|
33
|
+
abi: MockQueryDecrypterAbi,
|
|
34
|
+
functionName: 'querySealOutput',
|
|
35
|
+
args: [ctHash, BigInt(utype), permissionWithBigInts],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (error != '') {
|
|
39
|
+
throw new CofhesdkError({
|
|
40
|
+
code: CofhesdkErrorCode.SealOutputFailed,
|
|
41
|
+
message: `mocks querySealOutput call failed: ${error}`,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (allowed == false) {
|
|
46
|
+
throw new CofhesdkError({
|
|
47
|
+
code: CofhesdkErrorCode.SealOutputFailed,
|
|
48
|
+
message: `mocks querySealOutput call failed: ACL Access Denied (NotAllowed)`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const sealedBigInt = BigInt(result);
|
|
53
|
+
const sealingKeyBigInt = BigInt(permission.sealingKey);
|
|
54
|
+
const unsealed = sealedBigInt ^ sealingKeyBigInt;
|
|
55
|
+
|
|
56
|
+
return unsealed;
|
|
57
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { hardhat } from '@/chains';
|
|
2
|
+
import { type Permit, PermitUtils } from '@/permits';
|
|
3
|
+
|
|
4
|
+
import { FheTypes, type UnsealedItem } from '../types.js';
|
|
5
|
+
import { getThresholdNetworkUrlOrThrow } from '../config.js';
|
|
6
|
+
import { CofhesdkError, CofhesdkErrorCode } from '../error.js';
|
|
7
|
+
import { permits } from '../permits.js';
|
|
8
|
+
import { isValidUtype, convertViaUtype } from './decryptUtils.js';
|
|
9
|
+
import { BaseBuilder, type BaseBuilderParams } from '../baseBuilder.js';
|
|
10
|
+
import { cofheMocksSealOutput } from './cofheMocksSealOutput.js';
|
|
11
|
+
// import { tnSealOutputV1 } from './tnSealOutputV1.js';
|
|
12
|
+
import { tnSealOutputV2 } from './tnSealOutputV2.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* API
|
|
16
|
+
*
|
|
17
|
+
* await client.decryptHandle(ctHash, utype)
|
|
18
|
+
* .setChainId(chainId)
|
|
19
|
+
* .setAccount(account)
|
|
20
|
+
* .setPermitHash(permitHash)
|
|
21
|
+
* .setPermit(permit)
|
|
22
|
+
* .decrypt()
|
|
23
|
+
*
|
|
24
|
+
* If chainId not set, uses client's chainId
|
|
25
|
+
* If account not set, uses client's account
|
|
26
|
+
* If permitHash not set, uses chainId and account to get active permit
|
|
27
|
+
* If permit is set, uses permit to decrypt regardless of chainId, account, or permitHash
|
|
28
|
+
*
|
|
29
|
+
* Returns the unsealed item.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
type DecryptHandlesBuilderParams<U extends FheTypes> = BaseBuilderParams & {
|
|
33
|
+
ctHash: bigint;
|
|
34
|
+
utype: U;
|
|
35
|
+
permitHash?: string;
|
|
36
|
+
permit?: Permit;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export class DecryptHandlesBuilder<U extends FheTypes> extends BaseBuilder {
|
|
40
|
+
private ctHash: bigint;
|
|
41
|
+
private utype: U;
|
|
42
|
+
private permitHash?: string;
|
|
43
|
+
private permit?: Permit;
|
|
44
|
+
|
|
45
|
+
constructor(params: DecryptHandlesBuilderParams<U>) {
|
|
46
|
+
super({
|
|
47
|
+
config: params.config,
|
|
48
|
+
publicClient: params.publicClient,
|
|
49
|
+
walletClient: params.walletClient,
|
|
50
|
+
chainId: params.chainId,
|
|
51
|
+
account: params.account,
|
|
52
|
+
requireConnected: params.requireConnected,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
this.ctHash = params.ctHash;
|
|
56
|
+
this.utype = params.utype;
|
|
57
|
+
this.permitHash = params.permitHash;
|
|
58
|
+
this.permit = params.permit;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param chainId - Chain to decrypt values from. Used to fetch the threshold network URL and use the correct permit.
|
|
63
|
+
*
|
|
64
|
+
* If not provided, the chainId will be fetched from the connected publicClient.
|
|
65
|
+
*
|
|
66
|
+
* Example:
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const unsealed = await decryptHandle(ctHash, utype)
|
|
69
|
+
* .setChainId(11155111)
|
|
70
|
+
* .decrypt();
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @returns The chainable DecryptHandlesBuilder instance.
|
|
74
|
+
*/
|
|
75
|
+
setChainId(chainId: number): DecryptHandlesBuilder<U> {
|
|
76
|
+
this.chainId = chainId;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getChainId(): number | undefined {
|
|
81
|
+
return this.chainId;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param account - Account to decrypt values from. Used to fetch the correct permit.
|
|
86
|
+
*
|
|
87
|
+
* If not provided, the account will be fetched from the connected walletClient.
|
|
88
|
+
*
|
|
89
|
+
* Example:
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const unsealed = await decryptHandle(ctHash, utype)
|
|
92
|
+
* .setAccount('0x1234567890123456789012345678901234567890')
|
|
93
|
+
* .decrypt();
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @returns The chainable DecryptHandlesBuilder instance.
|
|
97
|
+
*/
|
|
98
|
+
setAccount(account: string): DecryptHandlesBuilder<U> {
|
|
99
|
+
this.account = account;
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getAccount(): string | undefined {
|
|
104
|
+
return this.account;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @param permitHash - Permit hash to decrypt values from. Used to fetch the correct permit.
|
|
109
|
+
*
|
|
110
|
+
* If not provided, the active permit for the chainId and account will be used.
|
|
111
|
+
* If `setPermit()` is called, it will be used regardless of chainId, account, or permitHash.
|
|
112
|
+
*
|
|
113
|
+
* Example:
|
|
114
|
+
* ```typescript
|
|
115
|
+
* const unsealed = await decryptHandle(ctHash, utype)
|
|
116
|
+
* .setPermitHash('0x1234567890123456789012345678901234567890')
|
|
117
|
+
* .decrypt();
|
|
118
|
+
* ```
|
|
119
|
+
*
|
|
120
|
+
* @returns The chainable DecryptHandlesBuilder instance.
|
|
121
|
+
*/
|
|
122
|
+
setPermitHash(permitHash: string): DecryptHandlesBuilder<U> {
|
|
123
|
+
this.permitHash = permitHash;
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getPermitHash(): string | undefined {
|
|
128
|
+
return this.permitHash;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @param permit - Permit to decrypt values with. If provided, it will be used regardless of chainId, account, or permitHash.
|
|
133
|
+
*
|
|
134
|
+
* If not provided, the permit will be determined by chainId, account, and permitHash.
|
|
135
|
+
*
|
|
136
|
+
* Example:
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const unsealed = await decryptHandle(ctHash, utype)
|
|
139
|
+
* .setPermit(permit)
|
|
140
|
+
* .decrypt();
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* @returns The chainable DecryptHandlesBuilder instance.
|
|
144
|
+
*/
|
|
145
|
+
setPermit(permit: Permit): DecryptHandlesBuilder<U> {
|
|
146
|
+
this.permit = permit;
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
getPermit(): Permit | undefined {
|
|
151
|
+
return this.permit;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private async getThresholdNetworkUrl(): Promise<string> {
|
|
155
|
+
this.assertChainId();
|
|
156
|
+
return getThresholdNetworkUrlOrThrow(this.config, this.chainId);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private validateUtypeOrThrow(): void {
|
|
160
|
+
if (!isValidUtype(this.utype))
|
|
161
|
+
throw new CofhesdkError({
|
|
162
|
+
code: CofhesdkErrorCode.InvalidUtype,
|
|
163
|
+
message: `Invalid utype to decrypt to`,
|
|
164
|
+
context: {
|
|
165
|
+
utype: this.utype,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private async getResolvedPermit(): Promise<Permit> {
|
|
171
|
+
if (this.permit) return this.permit;
|
|
172
|
+
|
|
173
|
+
this.assertChainId();
|
|
174
|
+
this.assertAccount();
|
|
175
|
+
|
|
176
|
+
// Fetch with permit hash
|
|
177
|
+
if (this.permitHash) {
|
|
178
|
+
const permit = await permits.getPermit(this.chainId, this.account, this.permitHash);
|
|
179
|
+
if (!permit) {
|
|
180
|
+
throw new CofhesdkError({
|
|
181
|
+
code: CofhesdkErrorCode.PermitNotFound,
|
|
182
|
+
message: `Permit with hash <${this.permitHash}> not found for account <${this.account}> and chainId <${this.chainId}>`,
|
|
183
|
+
hint: 'Ensure the permit exists and is valid.',
|
|
184
|
+
context: {
|
|
185
|
+
chainId: this.chainId,
|
|
186
|
+
account: this.account,
|
|
187
|
+
permitHash: this.permitHash,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
return permit;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Fetch with active permit
|
|
195
|
+
const permit = await permits.getActivePermit(this.chainId, this.account);
|
|
196
|
+
if (!permit) {
|
|
197
|
+
throw new CofhesdkError({
|
|
198
|
+
code: CofhesdkErrorCode.PermitNotFound,
|
|
199
|
+
message: `Active permit not found for chainId <${this.chainId}> and account <${this.account}>`,
|
|
200
|
+
hint: 'Ensure a permit exists for this account on this chain.',
|
|
201
|
+
context: {
|
|
202
|
+
chainId: this.chainId,
|
|
203
|
+
account: this.account,
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return permit;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* On hardhat, interact with MockZkVerifier contract instead of CoFHE
|
|
212
|
+
*/
|
|
213
|
+
private async mocksSealOutput(permit: Permit): Promise<bigint> {
|
|
214
|
+
this.assertPublicClient();
|
|
215
|
+
|
|
216
|
+
const mocksSealOutputDelay = this.config.mocks.sealOutputDelay;
|
|
217
|
+
return cofheMocksSealOutput(this.ctHash, this.utype, permit, this.publicClient, mocksSealOutputDelay);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* In the production context, perform a true decryption with the CoFHE coprocessor.
|
|
222
|
+
*/
|
|
223
|
+
private async productionSealOutput(permit: Permit): Promise<bigint> {
|
|
224
|
+
this.assertChainId();
|
|
225
|
+
this.assertPublicClient();
|
|
226
|
+
|
|
227
|
+
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
228
|
+
const permission = PermitUtils.getPermission(permit, true);
|
|
229
|
+
// const sealed = await tnSealOutputV1(this.ctHash, this.chainId, permission, thresholdNetworkUrl);
|
|
230
|
+
const sealed = await tnSealOutputV2(this.ctHash, this.chainId, permission, thresholdNetworkUrl);
|
|
231
|
+
return PermitUtils.unseal(permit, sealed);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Final step of the decryption process. MUST BE CALLED LAST IN THE CHAIN.
|
|
236
|
+
*
|
|
237
|
+
* This will:
|
|
238
|
+
* - Use a permit based on provided permit OR chainId + account + permitHash
|
|
239
|
+
* - Check permit validity
|
|
240
|
+
* - Call CoFHE `/sealoutput` with the permit, which returns a sealed (encrypted) item
|
|
241
|
+
* - Unseal the sealed item with the permit
|
|
242
|
+
* - Return the unsealed item
|
|
243
|
+
*
|
|
244
|
+
* Example:
|
|
245
|
+
* ```typescript
|
|
246
|
+
* const unsealed = await decryptHandle(ctHash, utype)
|
|
247
|
+
* .setChainId(11155111) // optional
|
|
248
|
+
* .setAccount('0x123...890') // optional
|
|
249
|
+
* .decrypt(); // execute
|
|
250
|
+
* ```
|
|
251
|
+
*
|
|
252
|
+
* @returns The unsealed item.
|
|
253
|
+
*/
|
|
254
|
+
async decrypt(): Promise<UnsealedItem<U>> {
|
|
255
|
+
// Ensure utype is valid
|
|
256
|
+
this.validateUtypeOrThrow();
|
|
257
|
+
|
|
258
|
+
// Resolve permit
|
|
259
|
+
const permit = await this.getResolvedPermit();
|
|
260
|
+
|
|
261
|
+
// Ensure permit validity
|
|
262
|
+
// TODO: This doesn't validate permit expiration
|
|
263
|
+
// TODO: This doesn't throw, returns a validation result instead
|
|
264
|
+
PermitUtils.validate(permit);
|
|
265
|
+
|
|
266
|
+
// TODO: Add this further validation step for the permit
|
|
267
|
+
// TODO: Ensure this throws if the permit is invalid
|
|
268
|
+
PermitUtils.isValid(permit);
|
|
269
|
+
|
|
270
|
+
// Extract chainId from signed permit
|
|
271
|
+
// Use this chainId to fetch the threshold network URL since this.chainId may be undefined
|
|
272
|
+
const chainId = permit._signedDomain!.chainId;
|
|
273
|
+
|
|
274
|
+
// Check permit validity on-chain
|
|
275
|
+
// TODO: PermitUtils.validateOnChain(permit, this.publicClient);
|
|
276
|
+
|
|
277
|
+
let unsealed: bigint;
|
|
278
|
+
|
|
279
|
+
if (chainId === hardhat.id) {
|
|
280
|
+
unsealed = await this.mocksSealOutput(permit);
|
|
281
|
+
} else {
|
|
282
|
+
unsealed = await this.productionSealOutput(permit);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return convertViaUtype(this.utype, unsealed);
|
|
286
|
+
}
|
|
287
|
+
}
|