@cofhe/sdk 0.3.1 → 0.4.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 +19 -0
- package/adapters/ethers6.test.ts +1 -1
- package/core/client.ts +6 -6
- package/core/clientTypes.ts +7 -7
- package/core/decrypt/cofheMocksDecryptForTx.ts +21 -79
- package/core/decrypt/cofheMocksDecryptForView.ts +3 -10
- package/core/decrypt/decryptForTxBuilder.ts +19 -14
- package/core/decrypt/decryptForViewBuilder.ts +9 -4
- package/core/decrypt/tnDecryptUtils.ts +65 -0
- package/core/decrypt/{tnDecrypt.ts → tnDecryptV1.ts} +10 -71
- package/core/decrypt/tnDecryptV2.ts +343 -0
- package/core/decrypt/tnSealOutputV2.ts +3 -3
- package/core/encrypt/cofheMocksZkVerifySign.ts +15 -11
- package/core/permits.ts +3 -3
- package/core/types.ts +8 -0
- package/dist/{chunk-2TPSCOW3.js → chunk-MXND5SVN.js} +275 -171
- package/dist/{clientTypes-Bhq7pCSA.d.cts → clientTypes-ACVWbrXL.d.cts} +22 -14
- package/dist/{clientTypes-6aTZPQ_4.d.ts → clientTypes-kkrRdawm.d.ts} +22 -14
- package/dist/core.cjs +274 -170
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1 -1
- package/dist/node.cjs +274 -170
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +1 -1
- package/dist/web.cjs +274 -170
- package/dist/web.d.cts +1 -1
- package/dist/web.d.ts +1 -1
- package/dist/web.js +1 -1
- package/node/client.test.ts +1 -1
- package/package.json +1 -1
- package/web/client.web.test.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @cofhe/sdk Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e446642: Switch `decryptForTx` to Threshold Network v2 decrypt (submit + poll)
|
|
8
|
+
|
|
9
|
+
## 0.3.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- d4e86ea: Aligns with CTA encrypted variables bytes32 representation.
|
|
14
|
+
|
|
15
|
+
- **@cofhe/hardhat-plugin**: `hre.cofhe.mocks.getTestBed()`, `getMockTaskManager()`, `getMockACL()`, `getMockThresholdNetwork()`, and `getMockZkVerifier()` now return typed contracts (typechain interfaces) instead of untyped `Contract`. `getPlaintext(ctHash)` and `expectPlaintext(ctHash, value)` now accept bytes32 ctHashes as `string` support cofhe-contracts 0.1.0 CTA changes.
|
|
16
|
+
- **@cofhe/mock-contracts**: Export typechain-generated contract types (`TestBed`, `MockACL`, `MockTaskManager`, `MockZkVerifier`, `MockThresholdNetwork`) for use with the hardhat plugin. Typechain is run from artifact ABIs only; factory files are not generated.
|
|
17
|
+
- **@cofhe/abi**: CTA-related types use `bytes32` (string) instead of `uint256`. Decryption and return-type helpers aligned with cofhe-contracts 0.1.0.
|
|
18
|
+
- **@cofhe/sdk**: Decryption APIs (`decryptForTx`, `decryptForView`, and related builders) now also accept `string` for ciphertext hashes (bytes32) as well as `bigint`.
|
|
19
|
+
|
|
20
|
+
- 0feaf3f: `cofheClient.decryptForTx` returns a ready-to-use signature
|
|
21
|
+
|
|
3
22
|
## 0.3.1
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/adapters/ethers6.test.ts
CHANGED
package/core/client.ts
CHANGED
|
@@ -146,7 +146,7 @@ export function createCofheClientBase<TConfig extends CofheConfig>(
|
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
function decryptForView<U extends FheTypes>(ctHash: bigint, utype: U): DecryptForViewBuilder<U> {
|
|
149
|
+
function decryptForView<U extends FheTypes>(ctHash: bigint | string, utype: U): DecryptForViewBuilder<U> {
|
|
150
150
|
const state = connectStore.getState();
|
|
151
151
|
|
|
152
152
|
return new DecryptForViewBuilder({
|
|
@@ -163,7 +163,7 @@ export function createCofheClientBase<TConfig extends CofheConfig>(
|
|
|
163
163
|
});
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
function decryptForTx(ctHash: bigint): DecryptForTxBuilderUnset {
|
|
166
|
+
function decryptForTx(ctHash: bigint | string): DecryptForTxBuilderUnset {
|
|
167
167
|
const state = connectStore.getState();
|
|
168
168
|
|
|
169
169
|
return new DecryptForTxBuilder({
|
|
@@ -250,22 +250,22 @@ export function createCofheClientBase<TConfig extends CofheConfig>(
|
|
|
250
250
|
},
|
|
251
251
|
|
|
252
252
|
// Retrieval methods (auto-fill chainId/account)
|
|
253
|
-
getPermit:
|
|
253
|
+
getPermit: (hash: string, chainId?: number, account?: string) => {
|
|
254
254
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
255
255
|
return permits.getPermit(_chainId, _account, hash);
|
|
256
256
|
},
|
|
257
257
|
|
|
258
|
-
getPermits:
|
|
258
|
+
getPermits: (chainId?: number, account?: string) => {
|
|
259
259
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
260
260
|
return permits.getPermits(_chainId, _account);
|
|
261
261
|
},
|
|
262
262
|
|
|
263
|
-
getActivePermit:
|
|
263
|
+
getActivePermit: (chainId?: number, account?: string) => {
|
|
264
264
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
265
265
|
return permits.getActivePermit(_chainId, _account);
|
|
266
266
|
},
|
|
267
267
|
|
|
268
|
-
getActivePermitHash:
|
|
268
|
+
getActivePermitHash: (chainId?: number, account?: string) => {
|
|
269
269
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
270
270
|
return permits.getActivePermitHash(_chainId, _account);
|
|
271
271
|
},
|
package/core/clientTypes.ts
CHANGED
|
@@ -48,9 +48,9 @@ export type CofheClient<TConfig extends CofheConfig = CofheConfig> = {
|
|
|
48
48
|
/**
|
|
49
49
|
* @deprecated Use `decryptForView` instead. Kept for backward compatibility.
|
|
50
50
|
*/
|
|
51
|
-
decryptHandle<U extends FheTypes>(ctHash: bigint, utype: U): DecryptForViewBuilder<U>;
|
|
52
|
-
decryptForView<U extends FheTypes>(ctHash: bigint, utype: U): DecryptForViewBuilder<U>;
|
|
53
|
-
decryptForTx(ctHash: bigint): DecryptForTxBuilderUnset;
|
|
51
|
+
decryptHandle<U extends FheTypes>(ctHash: bigint | string, utype: U): DecryptForViewBuilder<U>;
|
|
52
|
+
decryptForView<U extends FheTypes>(ctHash: bigint | string, utype: U): DecryptForViewBuilder<U>;
|
|
53
|
+
decryptForTx(ctHash: bigint | string): DecryptForTxBuilderUnset;
|
|
54
54
|
permits: CofheClientPermits;
|
|
55
55
|
};
|
|
56
56
|
|
|
@@ -84,10 +84,10 @@ export type CofheClientPermits = {
|
|
|
84
84
|
) => Promise<RecipientPermit>;
|
|
85
85
|
|
|
86
86
|
// Retrieval methods (chainId/account optional)
|
|
87
|
-
getPermit: (hash: string, chainId?: number, account?: string) =>
|
|
88
|
-
getPermits: (chainId?: number, account?: string) =>
|
|
89
|
-
getActivePermit: (chainId?: number, account?: string) =>
|
|
90
|
-
getActivePermitHash: (chainId?: number, account?: string) =>
|
|
87
|
+
getPermit: (hash: string, chainId?: number, account?: string) => Permit | undefined;
|
|
88
|
+
getPermits: (chainId?: number, account?: string) => Record<string, Permit>;
|
|
89
|
+
getActivePermit: (chainId?: number, account?: string) => Permit | undefined;
|
|
90
|
+
getActivePermitHash: (chainId?: number, account?: string) => string | undefined;
|
|
91
91
|
|
|
92
92
|
// Get or create methods (get active or create new, chainId/account optional)
|
|
93
93
|
getOrCreateSelfPermit: (chainId?: number, account?: string, options?: CreateSelfPermitOptions) => Promise<Permit>;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type Permit, PermitUtils } from '@/permits';
|
|
2
2
|
|
|
3
|
-
import { encodePacked, keccak256,
|
|
3
|
+
import { encodePacked, keccak256, type PublicClient } from 'viem';
|
|
4
4
|
import { sign } from 'viem/accounts';
|
|
5
|
-
import { sleep } from '../utils.js';
|
|
6
5
|
import { MockThresholdNetworkAbi } from './MockThresholdNetworkAbi.js';
|
|
7
6
|
import { FheTypes } from '../types.js';
|
|
8
7
|
import { CofheError, CofheErrorCode } from '../error.js';
|
|
@@ -10,25 +9,23 @@ import { MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY } from '../consts.js';
|
|
|
10
9
|
import { MOCKS_THRESHOLD_NETWORK_ADDRESS } from '../consts.js';
|
|
11
10
|
|
|
12
11
|
export type DecryptForTxMocksResult = {
|
|
13
|
-
ctHash: bigint;
|
|
12
|
+
ctHash: bigint | string;
|
|
14
13
|
decryptedValue: bigint;
|
|
15
|
-
signature: string
|
|
14
|
+
signature: `0x${string}`;
|
|
16
15
|
};
|
|
17
16
|
|
|
18
17
|
export async function cofheMocksDecryptForTx(
|
|
19
|
-
ctHash: bigint,
|
|
18
|
+
ctHash: bigint | string,
|
|
20
19
|
utype: FheTypes,
|
|
21
20
|
permit: Permit | null,
|
|
22
|
-
publicClient: PublicClient
|
|
23
|
-
mocksDecryptForTxDelay: number
|
|
21
|
+
publicClient: PublicClient
|
|
24
22
|
): Promise<DecryptForTxMocksResult> {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (mocksDecryptForTxDelay > 0) await sleep(mocksDecryptForTxDelay);
|
|
23
|
+
let allowed: boolean;
|
|
24
|
+
let error: string;
|
|
25
|
+
let decryptedValue: bigint;
|
|
29
26
|
|
|
27
|
+
// With permit
|
|
30
28
|
if (permit !== null) {
|
|
31
|
-
// With permit
|
|
32
29
|
let permission = PermitUtils.getPermission(permit, true);
|
|
33
30
|
const permissionWithBigInts = {
|
|
34
31
|
...permission,
|
|
@@ -36,66 +33,22 @@ export async function cofheMocksDecryptForTx(
|
|
|
36
33
|
validatorId: BigInt(permission.validatorId),
|
|
37
34
|
};
|
|
38
35
|
|
|
39
|
-
|
|
36
|
+
[allowed, error, decryptedValue] = await publicClient.readContract({
|
|
40
37
|
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
41
38
|
abi: MockThresholdNetworkAbi,
|
|
42
39
|
functionName: 'decryptForTxWithPermit',
|
|
43
|
-
args: [ctHash, permissionWithBigInts],
|
|
40
|
+
args: [BigInt(ctHash), permissionWithBigInts],
|
|
44
41
|
});
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (allowed == false) {
|
|
54
|
-
throw new CofheError({
|
|
55
|
-
code: CofheErrorCode.DecryptFailed,
|
|
56
|
-
message: `mocks decryptForTx call failed: ACL Access Denied (NotAllowed)`,
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// decryptForTx returns plaintext directly (no sealing/unsealing needed)
|
|
61
|
-
// Generate a mock threshold network signature (in production, this would be the actual signature)
|
|
62
|
-
// The signature must be valid for MockTaskManager verification.
|
|
63
|
-
const chainId = await publicClient.getChainId();
|
|
64
|
-
const ctHashBigInt = BigInt(ctHash);
|
|
65
|
-
const resultBigInt = BigInt(result);
|
|
66
|
-
const encryptionType = Number((ctHashBigInt & (0x7fn << 8n)) >> 8n);
|
|
67
|
-
|
|
68
|
-
// Matches Solidity: keccak256(abi.encodePacked(result, uint32(enc_type), uint64(chainId), bytes32(ctHash)))
|
|
69
|
-
const ctHashBytes32 = pad(toHex(ctHashBigInt), { size: 32 }) as Hex;
|
|
70
|
-
const packed = encodePacked(
|
|
71
|
-
['uint256', 'uint32', 'uint64', 'bytes32'],
|
|
72
|
-
[resultBigInt, encryptionType, BigInt(chainId), ctHashBytes32]
|
|
73
|
-
);
|
|
74
|
-
const messageHash = keccak256(packed);
|
|
75
|
-
|
|
76
|
-
// Raw digest signature (no EIP-191 prefix). Must verify against OpenZeppelin ECDSA.recover(messageHash, signature).
|
|
77
|
-
const signatureHex = await sign({
|
|
78
|
-
hash: messageHash,
|
|
79
|
-
privateKey: MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY,
|
|
80
|
-
to: 'hex',
|
|
42
|
+
} else {
|
|
43
|
+
// Without permit (global allowance)
|
|
44
|
+
[allowed, error, decryptedValue] = await publicClient.readContract({
|
|
45
|
+
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
46
|
+
abi: MockThresholdNetworkAbi,
|
|
47
|
+
functionName: 'decryptForTxWithoutPermit',
|
|
48
|
+
args: [BigInt(ctHash)],
|
|
81
49
|
});
|
|
82
|
-
const signature = signatureHex.slice(2); // no 0x prefix
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
ctHash,
|
|
86
|
-
decryptedValue: BigInt(result),
|
|
87
|
-
signature,
|
|
88
|
-
};
|
|
89
50
|
}
|
|
90
51
|
|
|
91
|
-
// Without permit (global allowance)
|
|
92
|
-
const [allowed, error, result] = await publicClient.readContract({
|
|
93
|
-
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
94
|
-
abi: MockThresholdNetworkAbi,
|
|
95
|
-
functionName: 'decryptForTxWithoutPermit',
|
|
96
|
-
args: [ctHash],
|
|
97
|
-
});
|
|
98
|
-
|
|
99
52
|
if (error != '') {
|
|
100
53
|
throw new CofheError({
|
|
101
54
|
code: CofheErrorCode.DecryptFailed,
|
|
@@ -113,30 +66,19 @@ export async function cofheMocksDecryptForTx(
|
|
|
113
66
|
// decryptForTx returns plaintext directly (no sealing/unsealing needed)
|
|
114
67
|
// Generate a mock threshold network signature (in production, this would be the actual signature)
|
|
115
68
|
// The signature must be valid for MockTaskManager verification.
|
|
116
|
-
const
|
|
117
|
-
const ctHashBigInt = BigInt(ctHash);
|
|
118
|
-
const resultBigInt = BigInt(result);
|
|
119
|
-
const encryptionType = Number((ctHashBigInt & (0x7fn << 8n)) >> 8n);
|
|
120
|
-
|
|
121
|
-
// Matches Solidity: keccak256(abi.encodePacked(result, uint32(enc_type), uint64(chainId), bytes32(ctHash)))
|
|
122
|
-
const ctHashBytes32 = pad(toHex(ctHashBigInt), { size: 32 }) as Hex;
|
|
123
|
-
const packed = encodePacked(
|
|
124
|
-
['uint256', 'uint32', 'uint64', 'bytes32'],
|
|
125
|
-
[resultBigInt, encryptionType, BigInt(chainId), ctHashBytes32]
|
|
126
|
-
);
|
|
69
|
+
const packed = encodePacked(['uint256', 'uint256'], [BigInt(ctHash), decryptedValue]);
|
|
127
70
|
const messageHash = keccak256(packed);
|
|
128
71
|
|
|
129
72
|
// Raw digest signature (no EIP-191 prefix). Must verify against OpenZeppelin ECDSA.recover(messageHash, signature).
|
|
130
|
-
const
|
|
73
|
+
const signature = await sign({
|
|
131
74
|
hash: messageHash,
|
|
132
75
|
privateKey: MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY,
|
|
133
76
|
to: 'hex',
|
|
134
77
|
});
|
|
135
|
-
const signature = signatureHex.slice(2); // no 0x prefix
|
|
136
78
|
|
|
137
79
|
return {
|
|
138
80
|
ctHash,
|
|
139
|
-
decryptedValue
|
|
81
|
+
decryptedValue,
|
|
140
82
|
signature,
|
|
141
83
|
};
|
|
142
84
|
}
|
|
@@ -1,24 +1,17 @@
|
|
|
1
1
|
import { type Permit, PermitUtils } from '@/permits';
|
|
2
2
|
|
|
3
3
|
import { type PublicClient } from 'viem';
|
|
4
|
-
import { sleep } from '../utils.js';
|
|
5
4
|
import { MockThresholdNetworkAbi } from './MockThresholdNetworkAbi.js';
|
|
6
5
|
import { FheTypes } from '../types.js';
|
|
7
6
|
import { CofheError, CofheErrorCode } from '../error.js';
|
|
8
7
|
import { MOCKS_THRESHOLD_NETWORK_ADDRESS } from '../consts.js';
|
|
9
8
|
|
|
10
9
|
export async function cofheMocksDecryptForView(
|
|
11
|
-
ctHash: bigint,
|
|
10
|
+
ctHash: bigint | string,
|
|
12
11
|
utype: FheTypes,
|
|
13
12
|
permit: Permit,
|
|
14
|
-
publicClient: PublicClient
|
|
15
|
-
mocksDecryptDelay: number
|
|
13
|
+
publicClient: PublicClient
|
|
16
14
|
): Promise<bigint> {
|
|
17
|
-
// Configurable delay before decrypting the output to simulate the CoFHE decrypt processing time
|
|
18
|
-
// Recommended 1000ms on web
|
|
19
|
-
// Recommended 0ms on hardhat (will be called during tests no need for fake delay)
|
|
20
|
-
if (mocksDecryptDelay > 0) await sleep(mocksDecryptDelay);
|
|
21
|
-
|
|
22
15
|
const permission = PermitUtils.getPermission(permit, true);
|
|
23
16
|
const permissionWithBigInts = {
|
|
24
17
|
...permission,
|
|
@@ -30,7 +23,7 @@ export async function cofheMocksDecryptForView(
|
|
|
30
23
|
address: MOCKS_THRESHOLD_NETWORK_ADDRESS,
|
|
31
24
|
abi: MockThresholdNetworkAbi,
|
|
32
25
|
functionName: 'querySealOutput',
|
|
33
|
-
args: [ctHash, BigInt(utype), permissionWithBigInts],
|
|
26
|
+
args: [BigInt(ctHash), BigInt(utype), permissionWithBigInts],
|
|
34
27
|
});
|
|
35
28
|
|
|
36
29
|
if (error != '') {
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import { hardhat } from '@/chains';
|
|
3
3
|
import { type Permit, type Permission, PermitUtils } from '@/permits';
|
|
4
4
|
|
|
5
|
-
import { FheTypes } from '../types
|
|
6
|
-
import { getThresholdNetworkUrlOrThrow } from '../config
|
|
7
|
-
import { CofheError, CofheErrorCode } from '../error
|
|
8
|
-
import { permits } from '../permits
|
|
9
|
-
import { BaseBuilder, type BaseBuilderParams } from '../baseBuilder
|
|
10
|
-
import { cofheMocksDecryptForTx } from './cofheMocksDecryptForTx
|
|
11
|
-
import { getPublicClientChainID } from '../utils
|
|
12
|
-
import {
|
|
5
|
+
import { FheTypes } from '../types';
|
|
6
|
+
import { getThresholdNetworkUrlOrThrow } from '../config';
|
|
7
|
+
import { CofheError, CofheErrorCode } from '../error';
|
|
8
|
+
import { permits } from '../permits';
|
|
9
|
+
import { BaseBuilder, type BaseBuilderParams } from '../baseBuilder';
|
|
10
|
+
import { cofheMocksDecryptForTx } from './cofheMocksDecryptForTx';
|
|
11
|
+
import { getPublicClientChainID, sleep } from '../utils';
|
|
12
|
+
import { tnDecryptV2 } from './tnDecryptV2';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* API
|
|
@@ -36,13 +36,13 @@ import { tnDecrypt } from './tnDecrypt.js';
|
|
|
36
36
|
type DecryptForTxPermitSelection = 'unset' | 'with-permit' | 'without-permit';
|
|
37
37
|
|
|
38
38
|
type DecryptForTxBuilderParams = BaseBuilderParams & {
|
|
39
|
-
ctHash: bigint;
|
|
39
|
+
ctHash: bigint | string;
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
export type DecryptForTxResult = {
|
|
43
|
-
ctHash: bigint;
|
|
43
|
+
ctHash: bigint | string;
|
|
44
44
|
decryptedValue: bigint;
|
|
45
|
-
signature: string
|
|
45
|
+
signature: `0x${string}`; // Threshold network signature for publishDecryptResult
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
/**
|
|
@@ -56,7 +56,7 @@ export type DecryptForTxBuilderUnset = Omit<DecryptForTxBuilder, 'execute'>;
|
|
|
56
56
|
export type DecryptForTxBuilderSelected = Omit<DecryptForTxBuilder, 'withPermit' | 'withoutPermit'>;
|
|
57
57
|
|
|
58
58
|
export class DecryptForTxBuilder extends BaseBuilder {
|
|
59
|
-
private ctHash: bigint;
|
|
59
|
+
private ctHash: bigint | string;
|
|
60
60
|
private permitHash?: string;
|
|
61
61
|
private permit?: Permit;
|
|
62
62
|
private permitSelection: DecryptForTxPermitSelection = 'unset';
|
|
@@ -271,8 +271,13 @@ export class DecryptForTxBuilder extends BaseBuilder {
|
|
|
271
271
|
private async mocksDecryptForTx(permit: Permit | null): Promise<DecryptForTxResult> {
|
|
272
272
|
this.assertPublicClient();
|
|
273
273
|
|
|
274
|
+
// Configurable delay before decrypting to simulate the CoFHE decrypt processing time
|
|
275
|
+
// Recommended 1000ms on web
|
|
276
|
+
// Recommended 0ms on hardhat (will be called during tests no need for fake delay)
|
|
274
277
|
const delay = this.config.mocks.decryptDelay;
|
|
275
|
-
|
|
278
|
+
if (delay > 0) await sleep(delay);
|
|
279
|
+
|
|
280
|
+
const result = await cofheMocksDecryptForTx(this.ctHash, 0 as FheTypes, permit, this.publicClient);
|
|
276
281
|
return result;
|
|
277
282
|
}
|
|
278
283
|
|
|
@@ -286,7 +291,7 @@ export class DecryptForTxBuilder extends BaseBuilder {
|
|
|
286
291
|
const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
|
|
287
292
|
|
|
288
293
|
const permission = permit ? PermitUtils.getPermission(permit, true) : null;
|
|
289
|
-
const { decryptedValue, signature } = await
|
|
294
|
+
const { decryptedValue, signature } = await tnDecryptV2(this.ctHash, this.chainId, permission, thresholdNetworkUrl);
|
|
290
295
|
|
|
291
296
|
return {
|
|
292
297
|
ctHash: this.ctHash,
|
|
@@ -11,7 +11,7 @@ import { BaseBuilder, type BaseBuilderParams } from '../baseBuilder.js';
|
|
|
11
11
|
import { cofheMocksDecryptForView } from './cofheMocksDecryptForView.js';
|
|
12
12
|
// import { tnSealOutputV1 } from './tnSealOutputV1.js';
|
|
13
13
|
import { tnSealOutputV2 } from './tnSealOutputV2.js';
|
|
14
|
-
import {
|
|
14
|
+
import { sleep } from '../utils.js';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* API
|
|
@@ -35,14 +35,14 @@ import { cofheMocksDecryptForTx } from './cofheMocksDecryptForTx.js';
|
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
37
|
type DecryptForViewBuilderParams<U extends FheTypes> = BaseBuilderParams & {
|
|
38
|
-
ctHash: bigint;
|
|
38
|
+
ctHash: bigint | string;
|
|
39
39
|
utype: U;
|
|
40
40
|
permitHash?: string;
|
|
41
41
|
permit?: Permit;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
|
|
45
|
-
private ctHash: bigint;
|
|
45
|
+
private ctHash: bigint | string;
|
|
46
46
|
private utype: U;
|
|
47
47
|
private permitHash?: string;
|
|
48
48
|
private permit?: Permit;
|
|
@@ -245,8 +245,13 @@ export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
|
|
|
245
245
|
private async mocksSealOutput(permit: Permit): Promise<bigint> {
|
|
246
246
|
this.assertPublicClient();
|
|
247
247
|
|
|
248
|
+
// Configurable delay before decrypting the output to simulate the CoFHE decrypt processing time
|
|
249
|
+
// Recommended 1000ms on web
|
|
250
|
+
// Recommended 0ms on hardhat (will be called during tests no need for fake delay)
|
|
248
251
|
const mocksDecryptDelay = this.config.mocks.decryptDelay;
|
|
249
|
-
|
|
252
|
+
if (mocksDecryptDelay > 0) await sleep(mocksDecryptDelay);
|
|
253
|
+
|
|
254
|
+
return cofheMocksDecryptForView(this.ctHash, this.utype, permit, this.publicClient);
|
|
250
255
|
}
|
|
251
256
|
|
|
252
257
|
/**
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { CofheError, CofheErrorCode } from '../error';
|
|
2
|
+
import { parseSignature, serializeSignature } from 'viem';
|
|
3
|
+
|
|
4
|
+
export function normalizeTnSignature(signature: unknown): `0x${string}` {
|
|
5
|
+
if (typeof signature !== 'string') {
|
|
6
|
+
throw new CofheError({
|
|
7
|
+
code: CofheErrorCode.DecryptReturnedNull,
|
|
8
|
+
message: 'decrypt response missing signature',
|
|
9
|
+
context: {
|
|
10
|
+
signature,
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const trimmed = signature.trim();
|
|
16
|
+
if (trimmed.length === 0) {
|
|
17
|
+
throw new CofheError({
|
|
18
|
+
code: CofheErrorCode.DecryptReturnedNull,
|
|
19
|
+
message: 'decrypt response returned empty signature',
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const prefixed = trimmed.startsWith('0x') ? (trimmed as `0x${string}`) : (`0x${trimmed}` as `0x${string}`);
|
|
24
|
+
const parsed = parseSignature(prefixed);
|
|
25
|
+
return serializeSignature(parsed);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function parseDecryptedBytesToBigInt(decrypted: unknown): bigint {
|
|
29
|
+
if (!Array.isArray(decrypted)) {
|
|
30
|
+
throw new CofheError({
|
|
31
|
+
code: CofheErrorCode.DecryptReturnedNull,
|
|
32
|
+
message: 'decrypt response field <decrypted> must be a byte array',
|
|
33
|
+
context: {
|
|
34
|
+
decrypted,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (decrypted.length === 0) {
|
|
40
|
+
throw new CofheError({
|
|
41
|
+
code: CofheErrorCode.DecryptReturnedNull,
|
|
42
|
+
message: 'decrypt response field <decrypted> was an empty byte array',
|
|
43
|
+
context: {
|
|
44
|
+
decrypted,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let hex = '';
|
|
50
|
+
for (const b of decrypted as unknown[]) {
|
|
51
|
+
if (typeof b !== 'number' || !Number.isInteger(b) || b < 0 || b > 255) {
|
|
52
|
+
throw new CofheError({
|
|
53
|
+
code: CofheErrorCode.DecryptReturnedNull,
|
|
54
|
+
message: 'decrypt response field <decrypted> contained a non-byte value',
|
|
55
|
+
context: {
|
|
56
|
+
badElement: b,
|
|
57
|
+
decrypted,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
hex += b.toString(16).padStart(2, '0');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return BigInt(`0x${hex}`);
|
|
65
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { type Permission } from '@/permits';
|
|
2
2
|
|
|
3
|
-
import { CofheError, CofheErrorCode } from '../error
|
|
3
|
+
import { CofheError, CofheErrorCode } from '../error';
|
|
4
|
+
import { normalizeTnSignature, parseDecryptedBytesToBigInt } from './tnDecryptUtils';
|
|
4
5
|
|
|
5
|
-
type
|
|
6
|
+
type TnDecryptResponseV1 = {
|
|
6
7
|
// TN returns bytes in big-endian order, e.g. [0,0,0,42]
|
|
7
8
|
decrypted: number[];
|
|
8
9
|
signature: string;
|
|
@@ -10,69 +11,7 @@ type TnDecryptResponse = {
|
|
|
10
11
|
error_message: string | null;
|
|
11
12
|
};
|
|
12
13
|
|
|
13
|
-
function
|
|
14
|
-
if (typeof signature !== 'string') {
|
|
15
|
-
throw new CofheError({
|
|
16
|
-
code: CofheErrorCode.DecryptReturnedNull,
|
|
17
|
-
message: 'decrypt response missing signature',
|
|
18
|
-
context: {
|
|
19
|
-
signature,
|
|
20
|
-
},
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const trimmed = signature.trim();
|
|
25
|
-
if (trimmed.length === 0) {
|
|
26
|
-
throw new CofheError({
|
|
27
|
-
code: CofheErrorCode.DecryptReturnedNull,
|
|
28
|
-
message: 'decrypt response returned empty signature',
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// SDK uses "no-0x" signatures in mocks/tests; normalize to that format.
|
|
33
|
-
return trimmed.startsWith('0x') ? trimmed.slice(2) : trimmed;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function parseDecryptedBytesToBigInt(decrypted: unknown): bigint {
|
|
37
|
-
if (!Array.isArray(decrypted)) {
|
|
38
|
-
throw new CofheError({
|
|
39
|
-
code: CofheErrorCode.DecryptReturnedNull,
|
|
40
|
-
message: 'decrypt response field <decrypted> must be a byte array',
|
|
41
|
-
context: {
|
|
42
|
-
decrypted,
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (decrypted.length === 0) {
|
|
48
|
-
throw new CofheError({
|
|
49
|
-
code: CofheErrorCode.DecryptReturnedNull,
|
|
50
|
-
message: 'decrypt response field <decrypted> was an empty byte array',
|
|
51
|
-
context: {
|
|
52
|
-
decrypted,
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
let hex = '';
|
|
58
|
-
for (const b of decrypted as unknown[]) {
|
|
59
|
-
if (typeof b !== 'number' || !Number.isInteger(b) || b < 0 || b > 255) {
|
|
60
|
-
throw new CofheError({
|
|
61
|
-
code: CofheErrorCode.DecryptReturnedNull,
|
|
62
|
-
message: 'decrypt response field <decrypted> contained a non-byte value',
|
|
63
|
-
context: {
|
|
64
|
-
badElement: b,
|
|
65
|
-
decrypted,
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
hex += b.toString(16).padStart(2, '0');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return BigInt(`0x${hex}`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function assertTnDecryptResponse(value: unknown): TnDecryptResponse {
|
|
14
|
+
function assertTnDecryptResponseV1(value: unknown): TnDecryptResponseV1 {
|
|
76
15
|
if (value == null || typeof value !== 'object') {
|
|
77
16
|
throw new CofheError({
|
|
78
17
|
code: CofheErrorCode.DecryptFailed,
|
|
@@ -126,18 +65,18 @@ function assertTnDecryptResponse(value: unknown): TnDecryptResponse {
|
|
|
126
65
|
};
|
|
127
66
|
}
|
|
128
67
|
|
|
129
|
-
export async function
|
|
130
|
-
ctHash: bigint,
|
|
68
|
+
export async function tnDecryptV1(
|
|
69
|
+
ctHash: bigint | string,
|
|
131
70
|
chainId: number,
|
|
132
71
|
permission: Permission | null,
|
|
133
72
|
thresholdNetworkUrl: string
|
|
134
|
-
): Promise<{ decryptedValue: bigint; signature: string }> {
|
|
73
|
+
): Promise<{ decryptedValue: bigint; signature: `0x${string}` }> {
|
|
135
74
|
const body: {
|
|
136
75
|
ct_tempkey: string;
|
|
137
76
|
host_chain_id: number;
|
|
138
77
|
permit?: Permission;
|
|
139
78
|
} = {
|
|
140
|
-
ct_tempkey: ctHash.toString(16).padStart(64, '0'),
|
|
79
|
+
ct_tempkey: BigInt(ctHash).toString(16).padStart(64, '0'),
|
|
141
80
|
host_chain_id: chainId,
|
|
142
81
|
};
|
|
143
82
|
|
|
@@ -211,7 +150,7 @@ export async function tnDecrypt(
|
|
|
211
150
|
});
|
|
212
151
|
}
|
|
213
152
|
|
|
214
|
-
const decryptResponse =
|
|
153
|
+
const decryptResponse = assertTnDecryptResponseV1(rawJson);
|
|
215
154
|
|
|
216
155
|
if (decryptResponse.error_message) {
|
|
217
156
|
throw new CofheError({
|
|
@@ -226,7 +165,7 @@ export async function tnDecrypt(
|
|
|
226
165
|
}
|
|
227
166
|
|
|
228
167
|
const decryptedValue = parseDecryptedBytesToBigInt(decryptResponse.decrypted);
|
|
229
|
-
const signature =
|
|
168
|
+
const signature = normalizeTnSignature(decryptResponse.signature);
|
|
230
169
|
|
|
231
170
|
return { decryptedValue, signature };
|
|
232
171
|
}
|