@cofhe/mock-contracts 0.8.0 → 1.0.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.
@@ -16,6 +16,7 @@ error InvalidInputsAmount(string operation, uint256 got, uint256 expected);
16
16
  error InvalidOperationInputs(string operation);
17
17
  error TooManyInputs(string operation, uint256 got, uint256 maxAllowed);
18
18
  error InvalidBytesLength(uint256 got, uint256 expected);
19
+ error LengthMismatch();
19
20
  // Type and security validation errors
20
21
  error InvalidTypeOrSecurityZone(string operation);
21
22
  error InvalidInputType(uint8 actual, uint8 expected);
@@ -31,7 +32,6 @@ error OnlyAggregatorAllowed(address caller);
31
32
 
32
33
  // Operation-specific errors
33
34
  error RandomFunctionNotSupported();
34
- error NotImplemented();
35
35
 
36
36
  library TMCommon {
37
37
  uint256 private constant HASH_MASK_FOR_METADATA = type(uint256).max - type(uint16).max; // 2 bytes reserved for metadata
@@ -159,10 +159,6 @@ library TMCommon {
159
159
  return uint256((SHIFTED_TYPE_MASK | SECURITY_ZONE_MASK) & hash);
160
160
  }
161
161
 
162
- function isTriviallyEncryptedFromHash(uint256 hash) internal pure returns (bool) {
163
- return (hash & TRIVIALLY_ENCRYPTED_MASK) == TRIVIALLY_ENCRYPTED_MASK;
164
- }
165
-
166
162
  function computeDecryptResultHash(
167
163
  uint256 ctHash,
168
164
  uint256 result,
@@ -211,8 +207,17 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
211
207
  }
212
208
 
213
209
  function setSecurityZones(int32 minSZ, int32 maxSZ) external onlyOwner {
214
- securityZoneMin = minSZ;
215
- securityZoneMax = maxSZ;
210
+ _setSecurityZones(minSZ, maxSZ);
211
+ }
212
+
213
+ /// @dev Same check as the real task manager's `_setSecurityZones`, the one write path for the bounds.
214
+ /// Without it `setSecurityZones(2, 1)` would be stored here while the real task manager reverts.
215
+ function _setSecurityZones(int32 newMin, int32 newMax) private {
216
+ if (newMin > newMax) {
217
+ revert InvalidSecurityZone(newMin, newMin, newMax);
218
+ }
219
+ securityZoneMin = newMin;
220
+ securityZoneMax = newMax;
216
221
  }
217
222
 
218
223
  function isInitialized() public view returns (bool) {
@@ -249,6 +254,9 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
249
254
  // Signer address for decrypt result verification (threshold network's signing key)
250
255
  address public decryptResultSigner;
251
256
 
257
+ // Counter mixed into generated random seeds, as in the real task manager
258
+ uint256 private randomCounter;
259
+
252
260
  // Storage contract for plaintext results of decrypt operations
253
261
  // PlaintextsStorage public plaintextsStorage;
254
262
 
@@ -370,9 +378,7 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
370
378
  }
371
379
 
372
380
  function checkAllowed(uint256 ctHash) internal view {
373
- if (!TMCommon.isTriviallyEncryptedFromHash(ctHash)) {
374
- if (!acl.isAllowed(ctHash, msg.sender)) revert ACLNotAllowed(ctHash, msg.sender);
375
- }
381
+ if (!acl.isAllowed(ctHash, msg.sender)) revert ACLNotAllowed(ctHash, msg.sender);
376
382
  }
377
383
 
378
384
  function isUnaryOperation(FunctionId funcId) internal pure returns (bool) {
@@ -481,6 +487,32 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
481
487
  validateFunctionInputTypes(funcId, functionName, encryptedHashes);
482
488
  }
483
489
 
490
+ /// @dev Same checks as the real task manager: the plaintext must fit the target type and the
491
+ /// security zone must be in range. Without them `FHE.asEuint8(256)` would be stored masked
492
+ /// to 0 here while the real task manager reverts.
493
+ function validateTrivialEncryptInputs(uint256[] memory extraInputs) internal view {
494
+ uint256 securityZone = extraInputs[2];
495
+ if (securityZone > uint256(int256(type(int32).max)) || !isValidSecurityZone(int32(int256(securityZone)))) {
496
+ revert InvalidSecurityZone(int32(int256(securityZone)), securityZoneMin, securityZoneMax);
497
+ }
498
+
499
+ uint256 value = extraInputs[0];
500
+ uint256 toType = extraInputs[1];
501
+ uint256 max;
502
+ if (toType == Utils.EUINT8_TFHE) max = type(uint8).max;
503
+ else if (toType == Utils.EUINT16_TFHE) max = type(uint16).max;
504
+ else if (toType == Utils.EUINT32_TFHE) max = type(uint32).max;
505
+ else if (toType == Utils.EUINT64_TFHE) max = type(uint64).max;
506
+ else if (toType == Utils.EUINT128_TFHE) max = type(uint128).max;
507
+ else if (toType == Utils.EADDRESS_TFHE) max = type(uint160).max;
508
+ else if (toType == Utils.EBOOL_TFHE) max = 1;
509
+ else return;
510
+
511
+ if (value > max) {
512
+ revert InvalidInputForFunction('trivialEncrypt', uint8(toType));
513
+ }
514
+ }
515
+
484
516
  function validateSelectInputs(uint256[] memory encryptedHashes) internal pure {
485
517
  if (encryptedHashes.length != 3) {
486
518
  revert InvalidInputsAmount('select', encryptedHashes.length, 3);
@@ -511,6 +543,9 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
511
543
  }
512
544
 
513
545
  validateInputs(encryptedHashes, funcId, operation);
546
+ if (funcId == FunctionId.trivialEncrypt) {
547
+ validateTrivialEncryptInputs(extraInputs);
548
+ }
514
549
  uint256[] memory inputs = TMCommon.combineInputs(encryptedHashes, extraInputs);
515
550
 
516
551
  int32 securityZone = getSecurityZone(funcId, encryptedHashes, extraInputs);
@@ -544,12 +579,7 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
544
579
  /// @notice Publish a signed decrypt result to the chain
545
580
  /// @dev Anyone with a valid signature from the decrypt network can call this
546
581
  function publishDecryptResult(uint256 ctHash, uint256 result, bytes calldata signature) external {
547
- _verifyDecryptResult(ctHash, result, signature, true);
548
- _decryptResultReady[ctHash] = true;
549
- _decryptResult[ctHash] = result;
550
-
551
- _decryptResultReadyTimestamp[ctHash] = uint64(block.timestamp);
552
- emit DecryptionResult(ctHash, result, msg.sender);
582
+ _publishDecryptResult(ctHash, result, signature);
553
583
  }
554
584
 
555
585
  function publishDecryptResultBatch(
@@ -557,14 +587,25 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
557
587
  uint256[] calldata results,
558
588
  bytes[] calldata signatures
559
589
  ) external {
560
- // Mock implementation: process each result individually
561
- // Note: publishDecryptResult is defined later in the contract
562
- for (uint256 i = 0; i < ctHashes.length; i++) {
563
- // Call via external interface to avoid forward reference issue
564
- this.publishDecryptResult(ctHashes[i], results[i], signatures[i]);
590
+ uint256 length = ctHashes.length;
591
+ if (results.length != length || signatures.length != length) revert LengthMismatch();
592
+
593
+ for (uint256 i = 0; i < length; i++) {
594
+ _publishDecryptResult(ctHashes[i], results[i], signatures[i]);
565
595
  }
566
596
  }
567
597
 
598
+ /// @dev Shared by both publish functions, so a batch keeps its caller as the `DecryptionResult` requestor,
599
+ /// like the real task manager. An external self-call would make the requestor this contract.
600
+ function _publishDecryptResult(uint256 ctHash, uint256 result, bytes calldata signature) private {
601
+ _verifyDecryptResult(ctHash, result, signature, true);
602
+ _decryptResultReady[ctHash] = true;
603
+ _decryptResult[ctHash] = result;
604
+
605
+ _decryptResultReadyTimestamp[ctHash] = uint64(block.timestamp);
606
+ emit DecryptionResult(ctHash, result, msg.sender);
607
+ }
608
+
568
609
  function verifyDecryptResult(uint256 ctHash, uint256 result, bytes calldata signature) external view returns (bool) {
569
610
  return _verifyDecryptResult(ctHash, result, signature, true);
570
611
  }
@@ -657,47 +698,36 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
657
698
  }
658
699
 
659
700
  function allow(uint256 ctHash, address account) external {
660
- if (!TMCommon.isTriviallyEncryptedFromHash(ctHash)) {
661
- acl.allow(ctHash, account, msg.sender);
701
+ acl.allow(ctHash, account, msg.sender);
662
702
 
663
- // NOTE: MOCK
664
- MOCK_trackedLogAllow(account == msg.sender ? 'FHE.allowThis' : 'FHE.allow', ctHash, account);
665
- }
703
+ // NOTE: MOCK
704
+ MOCK_trackedLogAllow(account == msg.sender ? 'FHE.allowThis' : 'FHE.allow', ctHash, account);
666
705
  }
667
706
 
668
707
  function allowGlobal(uint256 ctHash) external {
669
- if (!TMCommon.isTriviallyEncryptedFromHash(ctHash)) {
670
- acl.allowGlobal(ctHash, msg.sender);
708
+ acl.allowGlobal(ctHash, msg.sender);
671
709
 
672
- // NOTE: MOCK
673
- MOCK_trackedLogAllow('FHE.allowGlobal', ctHash, msg.sender);
674
- }
710
+ // NOTE: MOCK
711
+ MOCK_trackedLogAllow('FHE.allowGlobal', ctHash, msg.sender);
675
712
  }
676
713
 
677
714
  function allowTransient(uint256 ctHash, address account) external {
678
- if (!TMCommon.isTriviallyEncryptedFromHash(ctHash)) {
679
- acl.allowTransient(ctHash, account, msg.sender);
715
+ acl.allowTransient(ctHash, account, msg.sender);
680
716
 
681
- // NOTE: MOCK
682
- MOCK_trackedLogAllow('FHE.allowTransient', ctHash, account);
683
- }
717
+ // NOTE: MOCK
718
+ MOCK_trackedLogAllow('FHE.allowTransient', ctHash, account);
684
719
  }
685
720
 
686
721
  function allowForDecryption(uint256 ctHash) external {
687
- if (!TMCommon.isTriviallyEncryptedFromHash(ctHash)) {
688
- uint256[] memory hashes = new uint256[](1);
689
- hashes[0] = ctHash;
690
- acl.allowForDecryption(hashes, msg.sender);
722
+ uint256[] memory hashes = new uint256[](1);
723
+ hashes[0] = ctHash;
724
+ acl.allowForDecryption(hashes, msg.sender);
691
725
 
692
- // NOTE: MOCK
693
- MOCK_trackedLogAllow('FHE.allowForDecryption', ctHash, msg.sender);
694
- }
726
+ // NOTE: MOCK
727
+ MOCK_trackedLogAllow('FHE.allowForDecryption', ctHash, msg.sender);
695
728
  }
696
729
 
697
730
  function isAllowed(uint256 ctHash, address account) external view returns (bool) {
698
- if (TMCommon.isTriviallyEncryptedFromHash(ctHash)) {
699
- return true;
700
- }
701
731
  return acl.isAllowed(ctHash, account);
702
732
  }
703
733
 
@@ -769,17 +799,11 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
769
799
  }
770
800
 
771
801
  function setSecurityZoneMax(int32 securityZone) external onlyOwner {
772
- if (securityZone < securityZoneMin) {
773
- revert InvalidSecurityZone(securityZone, securityZoneMin, securityZoneMax);
774
- }
775
- securityZoneMax = securityZone;
802
+ _setSecurityZones(securityZoneMin, securityZone);
776
803
  }
777
804
 
778
805
  function setSecurityZoneMin(int32 securityZone) external onlyOwner {
779
- if (securityZone > securityZoneMax) {
780
- revert InvalidSecurityZone(securityZone, securityZoneMin, securityZoneMax);
781
- }
782
- securityZoneMin = securityZone;
806
+ _setSecurityZones(securityZone, securityZoneMax);
783
807
  }
784
808
 
785
809
  function setACLContract(address _aclAddress) external onlyOwner {
@@ -813,12 +837,46 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
813
837
  // Stub implementations for new ITaskManager interface methods (inc PR #48)
814
838
 
815
839
  function createRandomTask(uint8 returnType, uint256 seed, int32 securityZone) external returns (uint256) {
816
- // Mock implementation: just return a pseudo-random hash based on seed
817
- return uint256(keccak256(abi.encode(returnType, seed, securityZone, block.timestamp)));
840
+ if (!isValidSecurityZone(securityZone)) {
841
+ revert InvalidSecurityZone(securityZone, securityZoneMin, securityZoneMax);
842
+ }
843
+
844
+ if (seed == 0) {
845
+ seed = uint256(
846
+ keccak256(
847
+ abi.encodePacked(blockhash(block.number - 1), block.timestamp, randomCounter, block.chainid, securityZone)
848
+ )
849
+ );
850
+ unchecked {
851
+ randomCounter++;
852
+ }
853
+ }
854
+
855
+ // Same handle derivation as the real task manager: msg.sender is part of the preimage.
856
+ uint256[] memory inputs = new uint256[](2);
857
+ inputs[0] = seed;
858
+ inputs[1] = uint256(uint160(msg.sender));
859
+
860
+ uint256 ctHash = TMCommon.calcPlaceholderKey(returnType, securityZone, inputs, FunctionId.random);
861
+ acl.allowTransient(ctHash, msg.sender, address(this));
862
+ emit TaskCreated(
863
+ ctHash,
864
+ Utils.functionIdToString(FunctionId.random),
865
+ seed,
866
+ uint256(uint32(securityZone)),
867
+ inputs[1]
868
+ );
869
+
870
+ // NOTE: MOCK - the real task manager leaves the value to the coprocessor; store one here.
871
+ (MockGasMode mode, uint256 startGas) = _mockGasTrackStart();
872
+ _set(ctHash, uint256(keccak256(abi.encode(ctHash))));
873
+ _mockGasTrackEnd(mode, startGas);
874
+
875
+ return ctHash;
818
876
  }
819
877
 
820
878
  function isPubliclyAllowed(uint256 ctHash) external view returns (bool) {
821
- revert NotImplemented();
879
+ return acl.globalAllowed(ctHash);
822
880
  }
823
881
 
824
882
  function verifyDecryptResultBatch(
@@ -826,7 +884,10 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
826
884
  uint256[] calldata results,
827
885
  bytes[] calldata signatures
828
886
  ) external view returns (bool) {
829
- for (uint256 i = 0; i < ctHashes.length; i++) {
887
+ uint256 length = ctHashes.length;
888
+ if (results.length != length || signatures.length != length) revert LengthMismatch();
889
+
890
+ for (uint256 i = 0; i < length; i++) {
830
891
  if (!_verifyDecryptResult(ctHashes[i], results[i], signatures[i], true)) {
831
892
  return false;
832
893
  }
@@ -839,8 +900,11 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
839
900
  uint256[] calldata results,
840
901
  bytes[] calldata signatures
841
902
  ) external view returns (bool[] memory) {
842
- bool[] memory out = new bool[](ctHashes.length);
843
- for (uint256 i = 0; i < ctHashes.length; i++) {
903
+ uint256 length = ctHashes.length;
904
+ if (results.length != length || signatures.length != length) revert LengthMismatch();
905
+
906
+ bool[] memory out = new bool[](length);
907
+ for (uint256 i = 0; i < length; i++) {
844
908
  out[i] = _verifyDecryptResult(ctHashes[i], results[i], signatures[i], false);
845
909
  }
846
910
  return out;
@@ -9,7 +9,7 @@ pragma solidity >=0.8.19 <0.9.0;
9
9
 
10
10
  import { MockACL } from './MockACL.sol';
11
11
  import { MockTaskManager } from './MockTaskManager.sol';
12
- import { MockPermissioned, ACP } from './Permissioned.sol';
12
+ import { ACP, PermissionedUpgradeable } from './Permissioned.sol';
13
13
 
14
14
  contract MockThresholdNetwork {
15
15
  MockTaskManager public mockTaskManager;
@@ -181,16 +181,16 @@ contract MockThresholdNetwork {
181
181
 
182
182
  function decodeLowLevelReversion(bytes memory data) public pure returns (string memory error) {
183
183
  bytes4 selector = bytes4(data);
184
- if (selector == MockPermissioned.PermissionInvalid_Expired.selector) {
184
+ if (selector == PermissionedUpgradeable.PermissionInvalid_Expired.selector) {
185
185
  return 'PermissionInvalid_Expired';
186
186
  }
187
- if (selector == MockPermissioned.PermissionInvalid_IssuerSignature.selector) {
187
+ if (selector == PermissionedUpgradeable.PermissionInvalid_IssuerSignature.selector) {
188
188
  return 'PermissionInvalid_IssuerSignature';
189
189
  }
190
- if (selector == MockPermissioned.PermissionInvalid_RecipientSignature.selector) {
190
+ if (selector == PermissionedUpgradeable.PermissionInvalid_RecipientSignature.selector) {
191
191
  return 'PermissionInvalid_RecipientSignature';
192
192
  }
193
- if (selector == MockPermissioned.PermissionInvalid_Disabled.selector) {
193
+ if (selector == PermissionedUpgradeable.PermissionInvalid_Disabled.selector) {
194
194
  return 'PermissionInvalid_Disabled';
195
195
  }
196
196
  // Handle other errors