@cofhe/mock-contracts 0.0.0-alpha-20260409113701

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.
@@ -0,0 +1,444 @@
1
+ // SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ pragma solidity >=0.8.25 <0.9.0;
3
+
4
+ import { Strings } from '@openzeppelin/contracts/utils/Strings.sol';
5
+ import { FHE } from '@fhenixprotocol/cofhe-contracts/FHE.sol';
6
+ import { FunctionId, Utils } from '@fhenixprotocol/cofhe-contracts/ICofhe.sol';
7
+ import { console } from 'hardhat/console.sol';
8
+
9
+ address constant ZK_VERIFIER_SIGNER_ADDRESS = 0x6E12D8C87503D4287c294f2Fdef96ACd9DFf6bd2;
10
+ uint256 constant ZK_VERIFIER_SIGNER_PRIVATE_KEY = 49099792800763675079532137679706322989817545357788440619111868498148356080914;
11
+
12
+ address constant DECRYPT_RESULT_SIGNER_ADDRESS = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8;
13
+ uint256 constant DECRYPT_RESULT_SIGNER_PRIVATE_KEY = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;
14
+
15
+ /**
16
+ * @dev Mock implementation of the CoFHE coprocessor, used to test FHE ops in isolation.
17
+ * Is inherited by MockTaskManager.
18
+ *
19
+ * It is responsible for storing a map of ctHash -> value
20
+ * and for performing the operations on the values.
21
+ *
22
+ * It is intended as a 1:1 drop-in replacement for the real CoFHE coprocessor, with the following differences:
23
+ * - AsyncCallbacks are called synchronously (with a mock 1-10 second delay).
24
+ * - Unencrypted values are available onchain via the `mockStorage` map.
25
+ *
26
+ * NOTE: This is not used in production
27
+ */
28
+ abstract contract MockCoFHE {
29
+ // Pulled from TMCommon
30
+ uint256 constant uintTypeMask = (type(uint8).max >> 1); // 0x7f - 7 bits reserved for uint type in the one before last byte
31
+ uint256 constant triviallyEncryptedMask = type(uint8).max - uintTypeMask; //0x80 1 bit reserved for isTriviallyEncrypted
32
+ uint256 constant shiftedTypeMask = uintTypeMask << 8; // 0x7f007 bits reserved for uint type in the one before last byte
33
+
34
+ bool public logOps = true;
35
+
36
+ mapping(uint256 => uint256) public mockStorage;
37
+ mapping(uint256 => bool) public inMockStorage;
38
+
39
+ error InputNotInMockStorage(uint256 ctHash);
40
+
41
+ // Used internally to check if we missed any operations in the mocks
42
+ error InvalidUnaryOperation(string operation);
43
+ error InvalidTwoInputOperation(string operation);
44
+ error InvalidThreeInputOperation(string operation);
45
+
46
+ // OPTIONS
47
+
48
+ function setLogOps(bool _logOps) public {
49
+ logOps = _logOps;
50
+ }
51
+
52
+ // Utils
53
+
54
+ function getUintTypeFromHash(uint256 hash) internal pure returns (uint8) {
55
+ return uint8((hash & shiftedTypeMask) >> 8);
56
+ }
57
+
58
+ function getUtypeStringFromHash(uint256 hash) internal pure returns (string memory) {
59
+ uint8 inputType = getUintTypeFromHash(hash);
60
+ if (inputType == Utils.EBOOL_TFHE) return 'ebool';
61
+ if (inputType == Utils.EUINT8_TFHE) return 'euint8';
62
+ if (inputType == Utils.EUINT16_TFHE) return 'euint16';
63
+ if (inputType == Utils.EUINT32_TFHE) return 'euint32';
64
+ if (inputType == Utils.EUINT64_TFHE) return 'euint64';
65
+ if (inputType == Utils.EUINT128_TFHE) return 'euint128';
66
+ if (inputType == Utils.EUINT256_TFHE) return 'euint256';
67
+ if (inputType == Utils.EADDRESS_TFHE) return 'eaddress';
68
+ return 'unknown';
69
+ }
70
+
71
+ function getUtypeBits(uint256 hash) internal pure returns (uint256) {
72
+ uint8 inputType = getUintTypeFromHash(hash);
73
+ if (inputType == Utils.EBOOL_TFHE) return 8;
74
+ if (inputType == Utils.EUINT8_TFHE) return 8;
75
+ if (inputType == Utils.EUINT16_TFHE) return 16;
76
+ if (inputType == Utils.EUINT32_TFHE) return 32;
77
+ if (inputType == Utils.EUINT64_TFHE) return 64;
78
+ if (inputType == Utils.EUINT128_TFHE) return 128;
79
+ if (inputType == Utils.EUINT256_TFHE) return 256;
80
+ if (inputType == Utils.EADDRESS_TFHE) return 160;
81
+ return 0;
82
+ }
83
+
84
+ function getUtypeMask(uint256 hash) internal pure returns (uint256) {
85
+ uint256 bits = getUtypeBits(hash);
86
+ unchecked {
87
+ return (1 << bits) - 1;
88
+ }
89
+ }
90
+
91
+ function removeFirstLetter(string memory str) public pure returns (string memory) {
92
+ bytes memory strBytes = bytes(str);
93
+ if (strBytes.length == 0) return '';
94
+ bytes memory result = new bytes(strBytes.length - 1);
95
+ for (uint i = 1; i < strBytes.length; i++) {
96
+ result[i - 1] = strBytes[i];
97
+ }
98
+ return string(result);
99
+ }
100
+
101
+ function getIsBoolTypeFromHash(uint256 hash) internal pure returns (bool) {
102
+ uint8 inputType = getUintTypeFromHash(hash);
103
+ return (inputType ^ Utils.EBOOL_TFHE) == 0;
104
+ }
105
+
106
+ function strEq(string memory _a, string memory _b) internal pure returns (bool) {
107
+ return keccak256(abi.encodePacked(_a)) == keccak256(abi.encodePacked(_b));
108
+ }
109
+
110
+ function opIs(string memory op, FunctionId fid) internal pure returns (bool) {
111
+ return strEq(op, Utils.functionIdToString(fid));
112
+ }
113
+
114
+ function sliceString(string memory str, uint start, uint length) public pure returns (string memory) {
115
+ bytes memory strBytes = bytes(str);
116
+ require(start + length <= strBytes.length, 'Out of bounds');
117
+
118
+ bytes memory result = new bytes(length);
119
+ for (uint i = 0; i < length; i++) {
120
+ result[i] = strBytes[start + i];
121
+ }
122
+
123
+ return string(result);
124
+ }
125
+
126
+ function logCtHash(uint256 ctHash) internal view returns (string memory) {
127
+ string memory hashStr = Strings.toString(ctHash);
128
+ uint256 length = bytes(hashStr).length;
129
+ if (length <= 6) {
130
+ return hashStr;
131
+ }
132
+
133
+ bool stored = inMockStorage[ctHash];
134
+ uint256 value = mockStorage[ctHash];
135
+ bool isBool = getIsBoolTypeFromHash(ctHash);
136
+
137
+ string memory valueString = isBool ? (value == 1 ? 'true' : 'false') : Strings.toString(value);
138
+
139
+ string memory truncated = string.concat(
140
+ getUtypeStringFromHash(ctHash),
141
+ '(',
142
+ sliceString(hashStr, 0, 4),
143
+ '..',
144
+ sliceString(hashStr, length - 4, 4),
145
+ ')[',
146
+ stored ? valueString : 'EMPTY',
147
+ ']'
148
+ );
149
+
150
+ return truncated;
151
+ }
152
+
153
+ string constant LOG_PREFIX = unicode'├ ';
154
+ string constant LOG_DIVIDER = unicode' | ';
155
+
156
+ function padRight(string memory input, uint256 length, bytes1 padChar) internal pure returns (string memory) {
157
+ bytes memory inputBytes = bytes(input);
158
+ if (inputBytes.length >= length) return input;
159
+
160
+ bytes memory padded = new bytes(length);
161
+ uint256 i = 0;
162
+ for (; i < inputBytes.length; i++) {
163
+ padded[i] = inputBytes[i];
164
+ }
165
+ for (; i < length; i++) {
166
+ padded[i] = padChar;
167
+ }
168
+ return string(padded);
169
+ }
170
+
171
+ function logOperation(string memory operation, string memory inputs, string memory output) internal view {
172
+ if (logOps)
173
+ console.log(string.concat(LOG_PREFIX, padRight(operation, 16, ' '), LOG_DIVIDER, inputs, ' => ', output));
174
+ }
175
+
176
+ function logAllow(string memory operation, uint256 ctHash, address account) internal view {
177
+ if (logOps)
178
+ console.log(
179
+ string.concat(
180
+ LOG_PREFIX,
181
+ padRight(operation, 16, ' '),
182
+ LOG_DIVIDER,
183
+ logCtHash(ctHash),
184
+ ' -> ',
185
+ Strings.toHexString(account)
186
+ )
187
+ );
188
+ }
189
+
190
+ // Storage functions
191
+
192
+ function _set(uint256 ctHash, uint256 value, bool log) internal {
193
+ mockStorage[ctHash] = value;
194
+ inMockStorage[ctHash] = true;
195
+
196
+ if (log) logOperation('set', '', logCtHash(ctHash));
197
+ }
198
+
199
+ function _set(uint256 ctHash, uint256 value) internal {
200
+ uint256 mask = getUtypeMask(ctHash);
201
+ _set(ctHash, value & mask, false);
202
+ }
203
+
204
+ function _set(uint256 ctHash, bool value) internal {
205
+ _set(ctHash, value ? 1 : 0);
206
+ }
207
+
208
+ function _get(uint256 ctHash) internal view returns (uint256) {
209
+ if (!inMockStorage[ctHash]) revert InputNotInMockStorage(ctHash);
210
+
211
+ uint256 mask = getUtypeMask(ctHash);
212
+ return mockStorage[ctHash] & mask;
213
+ }
214
+
215
+ // Public functions
216
+
217
+ function MOCK_setInEuintKey(uint256 ctHash, uint256 value) public {
218
+ _set(ctHash, value);
219
+ }
220
+
221
+ // Mock Log
222
+
223
+ function MOCK_logAllow(string memory operation, uint256 ctHash, address account) public view {
224
+ logAllow(operation, ctHash, account);
225
+ }
226
+
227
+ // Mock functions
228
+
229
+ function MOCK_verifyKeyInStorage(uint256 ctHash) internal view {
230
+ if (!inMockStorage[ctHash]) revert InputNotInMockStorage(ctHash);
231
+ }
232
+
233
+ function MOCK_unaryOperation(uint256 ctHash, string memory operation, uint256 input) internal {
234
+ if (opIs(operation, FunctionId.random)) {
235
+ _set(ctHash, uint256(blockhash(block.number - 1)));
236
+ logOperation('FHE.random', '', logCtHash(ctHash));
237
+ return;
238
+ }
239
+ if (opIs(operation, FunctionId.cast)) {
240
+ _set(ctHash, _get(input));
241
+ logOperation('FHE.cast', logCtHash(input), logCtHash(ctHash));
242
+ return;
243
+ }
244
+ if (opIs(operation, FunctionId.not)) {
245
+ bool inputIsTruthy = _get(input) == 1;
246
+ _set(ctHash, !inputIsTruthy);
247
+ logOperation('FHE.not', logCtHash(input), logCtHash(ctHash));
248
+ return;
249
+ }
250
+ if (opIs(operation, FunctionId.square)) {
251
+ unchecked {
252
+ _set(ctHash, _get(input) * _get(input));
253
+ }
254
+ logOperation('FHE.square', string.concat(logCtHash(input), ' * ', logCtHash(input)), logCtHash(ctHash));
255
+ return;
256
+ }
257
+ revert InvalidUnaryOperation(operation);
258
+ }
259
+
260
+ function MOCK_twoInputOperation(uint256 ctHash, string memory operation, uint256 input1, uint256 input2) internal {
261
+ if (opIs(operation, FunctionId.sub)) {
262
+ unchecked {
263
+ _set(ctHash, _get(input1) - _get(input2));
264
+ }
265
+ logOperation('FHE.sub', string.concat(logCtHash(input1), ' - ', logCtHash(input2)), logCtHash(ctHash));
266
+ return;
267
+ }
268
+ if (opIs(operation, FunctionId.add)) {
269
+ unchecked {
270
+ _set(ctHash, _get(input1) + _get(input2));
271
+ }
272
+ logOperation('FHE.add', string.concat(logCtHash(input1), ' + ', logCtHash(input2)), logCtHash(ctHash));
273
+ return;
274
+ }
275
+ if (opIs(operation, FunctionId.xor)) {
276
+ unchecked {
277
+ _set(ctHash, _get(input1) ^ _get(input2));
278
+ }
279
+ logOperation('FHE.xor', string.concat(logCtHash(input1), ' ^ ', logCtHash(input2)), logCtHash(ctHash));
280
+ return;
281
+ }
282
+ if (opIs(operation, FunctionId.and)) {
283
+ unchecked {
284
+ _set(ctHash, _get(input1) & _get(input2));
285
+ }
286
+ logOperation('FHE.and', string.concat(logCtHash(input1), ' & ', logCtHash(input2)), logCtHash(ctHash));
287
+ return;
288
+ }
289
+ if (opIs(operation, FunctionId.or)) {
290
+ unchecked {
291
+ _set(ctHash, _get(input1) | _get(input2));
292
+ }
293
+ logOperation('FHE.or', string.concat(logCtHash(input1), ' | ', logCtHash(input2)), logCtHash(ctHash));
294
+ return;
295
+ }
296
+ if (opIs(operation, FunctionId.div)) {
297
+ uint256 cleartext2 = _get(input2);
298
+ if (cleartext2 == 0) {
299
+ _set(ctHash, type(uint256).max);
300
+ } else {
301
+ unchecked {
302
+ _set(ctHash, _get(input1) / cleartext2);
303
+ }
304
+ }
305
+ logOperation('FHE.div', string.concat(logCtHash(input1), ' / ', logCtHash(input2)), logCtHash(ctHash));
306
+ return;
307
+ }
308
+ if (opIs(operation, FunctionId.rem)) {
309
+ unchecked {
310
+ _set(ctHash, _get(input1) % _get(input2));
311
+ }
312
+ logOperation('FHE.rem', string.concat(logCtHash(input1), ' % ', logCtHash(input2)), logCtHash(ctHash));
313
+ return;
314
+ }
315
+ if (opIs(operation, FunctionId.mul)) {
316
+ unchecked {
317
+ _set(ctHash, _get(input1) * _get(input2));
318
+ }
319
+ logOperation('FHE.mul', string.concat(logCtHash(input1), ' * ', logCtHash(input2)), logCtHash(ctHash));
320
+ return;
321
+ }
322
+ if (opIs(operation, FunctionId.shl)) {
323
+ unchecked {
324
+ _set(ctHash, _get(input1) << _get(input2));
325
+ }
326
+ logOperation('FHE.shl', string.concat(logCtHash(input1), ' << ', logCtHash(input2)), logCtHash(ctHash));
327
+ return;
328
+ }
329
+ if (opIs(operation, FunctionId.shr)) {
330
+ unchecked {
331
+ _set(ctHash, _get(input1) >> _get(input2));
332
+ }
333
+ logOperation('FHE.shr', string.concat(logCtHash(input1), ' >> ', logCtHash(input2)), logCtHash(ctHash));
334
+ return;
335
+ }
336
+ if (opIs(operation, FunctionId.gte)) {
337
+ _set(ctHash, _get(input1) >= _get(input2));
338
+ logOperation('FHE.gte', string.concat(logCtHash(input1), ' >= ', logCtHash(input2)), logCtHash(ctHash));
339
+ return;
340
+ }
341
+ if (opIs(operation, FunctionId.lte)) {
342
+ _set(ctHash, _get(input1) <= _get(input2));
343
+ logOperation('FHE.lte', string.concat(logCtHash(input1), ' <= ', logCtHash(input2)), logCtHash(ctHash));
344
+ return;
345
+ }
346
+ if (opIs(operation, FunctionId.lt)) {
347
+ _set(ctHash, _get(input1) < _get(input2));
348
+ logOperation('FHE.lt', string.concat(logCtHash(input1), ' < ', logCtHash(input2)), logCtHash(ctHash));
349
+ return;
350
+ }
351
+ if (opIs(operation, FunctionId.gt)) {
352
+ _set(ctHash, _get(input1) > _get(input2));
353
+ logOperation('FHE.gt', string.concat(logCtHash(input1), ' > ', logCtHash(input2)), logCtHash(ctHash));
354
+ return;
355
+ }
356
+ if (opIs(operation, FunctionId.min)) {
357
+ uint256 min;
358
+ unchecked {
359
+ min = _get(input1) < _get(input2) ? _get(input1) : _get(input2);
360
+ }
361
+ _set(ctHash, min);
362
+
363
+ logOperation(
364
+ 'FHE.min',
365
+ string.concat('min(', logCtHash(input1), ', ', logCtHash(input2), ')'),
366
+ logCtHash(ctHash)
367
+ );
368
+ return;
369
+ }
370
+ if (opIs(operation, FunctionId.max)) {
371
+ uint256 max;
372
+ unchecked {
373
+ max = _get(input1) > _get(input2) ? _get(input1) : _get(input2);
374
+ }
375
+ _set(ctHash, max);
376
+
377
+ logOperation(
378
+ 'FHE.max',
379
+ string.concat('max(', logCtHash(input1), ', ', logCtHash(input2), ')'),
380
+ logCtHash(ctHash)
381
+ );
382
+ return;
383
+ }
384
+ if (opIs(operation, FunctionId.eq)) {
385
+ _set(ctHash, _get(input1) == _get(input2));
386
+
387
+ logOperation('FHE.eq', string.concat(logCtHash(input1), ' == ', logCtHash(input2)), logCtHash(ctHash));
388
+ return;
389
+ }
390
+ if (opIs(operation, FunctionId.ne)) {
391
+ _set(ctHash, _get(input1) != _get(input2));
392
+
393
+ logOperation('FHE.ne', string.concat(logCtHash(input1), ' != ', logCtHash(input2)), logCtHash(ctHash));
394
+ return;
395
+ }
396
+ if (opIs(operation, FunctionId.rol)) {
397
+ unchecked {
398
+ _set(ctHash, _get(input1) << _get(input2));
399
+ }
400
+
401
+ logOperation('FHE.rol', string.concat(logCtHash(input1), ' << ', logCtHash(input2)), logCtHash(ctHash));
402
+ return;
403
+ }
404
+ if (opIs(operation, FunctionId.ror)) {
405
+ unchecked {
406
+ _set(ctHash, _get(input1) >> _get(input2));
407
+ }
408
+
409
+ logOperation('FHE.ror', string.concat(logCtHash(input1), ' >> ', logCtHash(input2)), logCtHash(ctHash));
410
+ return;
411
+ }
412
+ revert InvalidTwoInputOperation(operation);
413
+ }
414
+
415
+ function MOCK_threeInputOperation(
416
+ uint256 ctHash,
417
+ string memory operation,
418
+ uint256 input1,
419
+ uint256 input2,
420
+ uint256 input3
421
+ ) internal {
422
+ if (opIs(operation, FunctionId.trivialEncrypt)) {
423
+ _set(ctHash, input1);
424
+
425
+ logOperation(
426
+ string.concat('FHE.asE', removeFirstLetter(getUtypeStringFromHash(ctHash))),
427
+ string.concat(removeFirstLetter(getUtypeStringFromHash(ctHash)), '(', Strings.toString(input1), ')'),
428
+ logCtHash(ctHash)
429
+ );
430
+ return;
431
+ }
432
+ if (opIs(operation, FunctionId.select)) {
433
+ _set(ctHash, _get(input1) == 1 ? _get(input2) : _get(input3));
434
+
435
+ logOperation(
436
+ 'FHE.select',
437
+ string.concat(logCtHash(input1), ' ? ', logCtHash(input2), ' : ', logCtHash(input3)),
438
+ logCtHash(ctHash)
439
+ );
440
+ return;
441
+ }
442
+ revert InvalidThreeInputOperation(operation);
443
+ }
444
+ }