@cofhe/mock-contracts 0.2.0 → 0.3.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 +26 -0
- package/README.md +63 -9
- package/contracts/MockACL.sol +4 -0
- package/contracts/MockTaskManager.sol +86 -1
- package/contracts/{MockQueryDecrypter.sol → MockThresholdNetwork.sol} +67 -1
- package/contracts/TestBed.sol +34 -2
- package/contracts/foundry/CoFheTest.sol +47 -27
- package/dist/index.d.mts +14 -11
- package/dist/index.d.ts +14 -11
- package/dist/index.js +397 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +397 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/MockACL.ts +65 -6
- package/src/MockTaskManager.ts +187 -5
- package/src/MockThresholdNetwork.ts +457 -0
- package/src/MockZkVerifier.ts +5 -6
- package/src/TestBed.ts +35 -13
- package/src/index.ts +1 -1
- package/src/types.ts +12 -5
- package/test/TestBed.t.sol +18 -7
- package/src/MockQueryDecrypter.ts +0 -353
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @cofhe/mock-contracts Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 35024b6: Remove `sdk` from function names and exported types. Rename:
|
|
8
|
+
|
|
9
|
+
- `createCofhesdkConfig` -> `createCofheConfig`
|
|
10
|
+
- `createCofhesdkClient` -> `createCofheClient`
|
|
11
|
+
- `hre.cofhesdk.*` -> `hre.cofhe.*`
|
|
12
|
+
- `hre.cofhesdk.createCofheConfig()` → `hre.cofhe.createConfig()`
|
|
13
|
+
- `hre.cofhesdk.createCofheClient()` → `hre.cofhe.createClient()`
|
|
14
|
+
- `hre.cofhesdk.createBatteriesIncludedCofheClient()` → `hre.cofhe.createClientWithBatteries()`
|
|
15
|
+
|
|
16
|
+
- 29c2401: implement decrypt-with-proof flows and related tests:
|
|
17
|
+
|
|
18
|
+
- Implement production `decryptForTx` backed by Threshold Network `POST /decrypt`, with explicit permit vs global-allowance selection.
|
|
19
|
+
- Rename mocks “Query Decrypter” -> “Threshold Network” and update SDK constants/contracts/artifacts accordingly.
|
|
20
|
+
- Extend mock contracts + hardhat plugin to publish & verify decryption results on-chain, and add end-to-end integration tests.
|
|
21
|
+
|
|
22
|
+
## 0.2.1
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- ac47e2f: Add `PermitUtils.checkValidityOnChain` to validate permits against the on-chain deployed ACL (source of truth).
|
|
27
|
+
- 0000d5e: Mock contracts deployed to alternate fixed addresses to avoid collision with hardhat pre-compiles.
|
|
28
|
+
|
|
3
29
|
## 0.2.0
|
|
4
30
|
|
|
5
31
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# cofhe/mock-contracts [![NPM Package][npm-badge]][npm] [![License: MIT][license-badge]][license]
|
|
2
2
|
|
|
3
3
|
[npm]: https://www.npmjs.com/package/@fhenixprotocol/cofhe-mock-contracts
|
|
4
4
|
[npm-badge]: https://img.shields.io/npm/v/@fhenixprotocol/cofhe-mock-contracts.svg
|
|
@@ -11,7 +11,7 @@ A mock smart contract library for testing CoFHE (Confidential Computing Framewor
|
|
|
11
11
|
|
|
12
12
|
- Mock implementations of core CoFHE contracts:
|
|
13
13
|
- MockTaskManager
|
|
14
|
-
-
|
|
14
|
+
- MockThresholdNetwork
|
|
15
15
|
- MockZkVerifier
|
|
16
16
|
- ACL (Access Control List)
|
|
17
17
|
- Synchronous operation simulation with mock delays
|
|
@@ -34,11 +34,67 @@ forge install fhenixprotocol/cofhe-mock-contracts
|
|
|
34
34
|
|
|
35
35
|
## Usages and Integrations
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
### Who is this for?
|
|
38
|
+
|
|
39
|
+
This package is intended for **developers building and testing CoFHE-enabled applications and smart contracts**.
|
|
40
|
+
|
|
41
|
+
Use these mocks when you want to:
|
|
42
|
+
|
|
43
|
+
- Run **local tests** without depending on the real CoFHE coprocessor infrastructure.
|
|
44
|
+
- Debug flows end-to-end (encrypt → submit → operate → decrypt) with fast iteration.
|
|
45
|
+
- Assert on results deterministically in CI.
|
|
46
|
+
|
|
47
|
+
Do **not** use these mocks for production deployments: they intentionally make testing convenient (e.g. storing plaintext on-chain for inspection) and therefore **do not provide real confidentiality guarantees**.
|
|
48
|
+
|
|
49
|
+
### Hardhat integration vs Foundry integration
|
|
50
|
+
|
|
51
|
+
Both integrations use the same underlying mock contracts, but they differ in **how mocks get deployed** and **how you interact with them**.
|
|
52
|
+
|
|
53
|
+
#### Hardhat (recommended for TS/SDK + Solidity tests)
|
|
54
|
+
|
|
55
|
+
Use this when you are already using **Hardhat** and/or want to run the **TypeScript SDK (`@cofhe/sdk`)** against a local chain.
|
|
56
|
+
|
|
57
|
+
- The `cofhesdk/hardhat-plugin` watches Hardhat `node` and `test` tasks.
|
|
58
|
+
- It automatically deploys the mocks to the Hardhat network at fixed addresses.
|
|
59
|
+
- The `cofheClient` (created with `createCofheClient(...)`) detects the mocks and routes CoFHE actions to them.
|
|
60
|
+
|
|
61
|
+
Minimal setup:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
// hardhat.config.ts
|
|
65
|
+
import 'cofhe-hardhat-plugin';
|
|
66
|
+
|
|
67
|
+
export default {
|
|
68
|
+
cofhe: {
|
|
69
|
+
logMocks: true, // optional
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Run:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx hardhat test
|
|
78
|
+
# or
|
|
79
|
+
npx hardhat node
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
If you want to assert on plaintext values in Hardhat tests, the plugin exposes helpers like `mock_expectPlaintext(...)` (see the hardhat-plugin README).
|
|
83
|
+
|
|
84
|
+
#### Foundry (recommended for Solidity-only tests)
|
|
85
|
+
|
|
86
|
+
Use this when you are writing tests in **Solidity** and running them with `forge test`.
|
|
87
|
+
|
|
88
|
+
- You typically inherit from the abstract `CoFheTest` helper to deploy/setup the necessary FHE mock environment.
|
|
89
|
+
- You use helper methods to create encrypted inputs and assert their underlying values.
|
|
90
|
+
|
|
91
|
+
> **Important**: You must set `isolate = true` in your `foundry.toml`. Without this setting, some variables may be used without proper permission checks, which will cause failures on production chains.
|
|
92
|
+
|
|
93
|
+
`@cofhe/sdk` is designed to work with mock contracts in a testing / hardhat environment. `cofhesdk/hardhat-plugin` deploys the mock contracts in this repo, and the `cofheClient` detects a testnet chain and interacts correctly using the mocks rather than the true CoFHE coprocessor.
|
|
38
94
|
|
|
39
95
|
When installed and imported in the `hardhat.config.ts`, `cofhesdk/hardhat-plugin` will watch for Hardhat `node` and `test` tasks, and will deploy the mocks to the hardhat testnet chain at fixed addresses.
|
|
40
96
|
|
|
41
|
-
Once deployed, interaction with the mock contracts is handled by the `
|
|
97
|
+
Once deployed, interaction with the mock contracts is handled by the `cofheClient` (created with `createCofheClient(...)`). The client checks for the existence of mock contracts at known addresses, and if they exist, marks the current connection as a testnet.
|
|
42
98
|
|
|
43
99
|
## Logging
|
|
44
100
|
|
|
@@ -71,20 +127,18 @@ The mocks are then responsible for mocking two actions:
|
|
|
71
127
|
1. Creating the signature.
|
|
72
128
|
2. Storing the plaintext value on-chain.
|
|
73
129
|
|
|
74
|
-
The `MockZkVerifier` contract handles the on-chain storage of encrypted inputs. The signature creation is handled automatically within `
|
|
130
|
+
The `MockZkVerifier` contract handles the on-chain storage of encrypted inputs. The signature creation is handled automatically within `cofheClient.encryptInputs` when executing against a testnet.
|
|
75
131
|
|
|
76
132
|
### Off-chain Decryption / Sealing
|
|
77
133
|
|
|
78
|
-
Off-chain decryption is performed by calling the `
|
|
134
|
+
Off-chain decryption is performed by calling the `cofheClient.decryptHandle` function with a valid `ctHash` and a valid `permit` [todo link].
|
|
79
135
|
|
|
80
136
|
When interacting with CoFHE this request is routed to the Threshold Network, which will perform the decryption operation, ultimately returning a decrypted result.
|
|
81
137
|
|
|
82
|
-
When working with the mocks, the `
|
|
138
|
+
When working with the mocks, the `cofheClient` will instead query the `MockThresholdNetwork` contract, which will verify the request `permit`, and return the decrypted result.
|
|
83
139
|
|
|
84
140
|
### Using Foundry
|
|
85
141
|
|
|
86
|
-
> **Important**: You must set `isolate = true` in your `foundry.toml`. Without this setting, some variables may be used without proper permission checks, which will cause failures on production chains.
|
|
87
|
-
|
|
88
142
|
Use abstract CoFheTest contract to automatically deploy all necessary FHE contracts for testing.
|
|
89
143
|
|
|
90
144
|
CoFheTest also exposes useful test methods such as
|
package/contracts/MockACL.sol
CHANGED
|
@@ -335,4 +335,8 @@ contract MockACL is MockPermissioned {
|
|
|
335
335
|
) public view withPermission(permission) returns (bool) {
|
|
336
336
|
return isAllowed(handle, permission.issuer);
|
|
337
337
|
}
|
|
338
|
+
|
|
339
|
+
function checkPermitValidity(Permission memory permission) public view withPermission(permission) returns (bool) {
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
338
342
|
}
|
|
@@ -30,6 +30,7 @@ error OnlyAggregatorAllowed(address caller);
|
|
|
30
30
|
|
|
31
31
|
// Operation-specific errors
|
|
32
32
|
error RandomFunctionNotSupported();
|
|
33
|
+
error NotImplemented();
|
|
33
34
|
|
|
34
35
|
library TMCommon {
|
|
35
36
|
uint256 private constant HASH_MASK_FOR_METADATA = type(uint256).max - type(uint16).max; // 2 bytes reserved for metadata
|
|
@@ -150,6 +151,7 @@ library TMCommon {
|
|
|
150
151
|
function getSecAndTypeFromHash(uint256 hash) internal pure returns (uint256) {
|
|
151
152
|
return uint256((SHIFTED_TYPE_MASK | SECURITY_ZONE_MASK) & hash);
|
|
152
153
|
}
|
|
154
|
+
|
|
153
155
|
function isTriviallyEncryptedFromHash(uint256 hash) internal pure returns (bool) {
|
|
154
156
|
return (hash & TRIVIALLY_ENCRYPTED_MASK) == TRIVIALLY_ENCRYPTED_MASK;
|
|
155
157
|
}
|
|
@@ -218,6 +220,9 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
|
|
|
218
220
|
|
|
219
221
|
address private verifierSigner;
|
|
220
222
|
|
|
223
|
+
// Signer address for decrypt result verification (threshold network's signing key)
|
|
224
|
+
address public decryptResultSigner;
|
|
225
|
+
|
|
221
226
|
// Storage contract for plaintext results of decrypt operations
|
|
222
227
|
// PlaintextsStorage public plaintextsStorage;
|
|
223
228
|
|
|
@@ -288,7 +293,7 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
|
|
|
288
293
|
// NOTE: MOCK
|
|
289
294
|
if (block.timestamp < _decryptResultReadyTimestamp[ctHash]) return (0, false);
|
|
290
295
|
|
|
291
|
-
return (
|
|
296
|
+
return (_decryptResult[ctHash], true);
|
|
292
297
|
}
|
|
293
298
|
|
|
294
299
|
function checkAllowed(uint256 ctHash) internal view {
|
|
@@ -460,6 +465,50 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
|
|
|
460
465
|
emit ProtocolNotification(ctHash, operation, errorMessage);
|
|
461
466
|
}
|
|
462
467
|
|
|
468
|
+
/// @notice Publish a signed decrypt result to the chain
|
|
469
|
+
/// @dev Anyone with a valid signature from the decrypt network can call this
|
|
470
|
+
function publishDecryptResult(uint256 ctHash, uint256 result, bytes calldata signature) external {
|
|
471
|
+
_verifyDecryptResult(ctHash, result, signature);
|
|
472
|
+
_decryptResultReady[ctHash] = true;
|
|
473
|
+
_decryptResult[ctHash] = result;
|
|
474
|
+
|
|
475
|
+
_decryptResultReadyTimestamp[ctHash] = uint64(block.timestamp);
|
|
476
|
+
emit DecryptionResult(ctHash, result, msg.sender);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function publishDecryptResultBatch(
|
|
480
|
+
uint256[] calldata ctHashes,
|
|
481
|
+
uint256[] calldata results,
|
|
482
|
+
bytes[] calldata signatures
|
|
483
|
+
) external {
|
|
484
|
+
// Mock implementation: process each result individually
|
|
485
|
+
// Note: publishDecryptResult is defined later in the contract
|
|
486
|
+
for (uint256 i = 0; i < ctHashes.length; i++) {
|
|
487
|
+
// Call via external interface to avoid forward reference issue
|
|
488
|
+
this.publishDecryptResult(ctHashes[i], results[i], signatures[i]);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function _verifyDecryptResult(uint256 ctHash, uint256 result, bytes calldata signature) private view {
|
|
493
|
+
if (decryptResultSigner == address(0)) revert InvalidAddress();
|
|
494
|
+
bytes32 messageHash = _computeDecryptResultHash(ctHash, result);
|
|
495
|
+
(address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecover(messageHash, signature);
|
|
496
|
+
|
|
497
|
+
if (err != ECDSA.RecoverError.NoError || recovered == address(0)) {
|
|
498
|
+
revert InvalidSignature();
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (recovered != decryptResultSigner) {
|
|
502
|
+
revert InvalidSigner(recovered, decryptResultSigner);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/// @notice Format: result (32) || enc_type (4) || chain_id (8) || ct_hash (32) = 76 bytes
|
|
507
|
+
function _computeDecryptResultHash(uint256 ctHash, uint256 result) private view returns (bytes32) {
|
|
508
|
+
uint8 encryptionType = TMCommon.getUintTypeFromHash(ctHash);
|
|
509
|
+
return keccak256(abi.encodePacked(result, uint32(encryptionType), uint64(block.chainid), bytes32(ctHash)));
|
|
510
|
+
}
|
|
511
|
+
|
|
463
512
|
function verifyType(uint8 ctType, uint8 desiredType) internal pure {
|
|
464
513
|
if (ctType != desiredType) {
|
|
465
514
|
revert InvalidInputType(ctType, desiredType);
|
|
@@ -550,6 +599,12 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
|
|
|
550
599
|
verifierSigner = signer;
|
|
551
600
|
}
|
|
552
601
|
|
|
602
|
+
/// @notice Set the authorized signer for decrypt results
|
|
603
|
+
/// @param signer The new signer address (must be non-zero for publishDecryptResult to work)
|
|
604
|
+
function setDecryptResultSigner(address signer) external onlyOwner {
|
|
605
|
+
decryptResultSigner = signer;
|
|
606
|
+
}
|
|
607
|
+
|
|
553
608
|
function setSecurityZoneMax(int32 securityZone) external onlyOwner {
|
|
554
609
|
if (securityZone < securityZoneMin) {
|
|
555
610
|
revert InvalidSecurityZone(securityZone, securityZoneMin, securityZoneMax);
|
|
@@ -590,4 +645,34 @@ contract MockTaskManager is ITaskManager, MockCoFHE {
|
|
|
590
645
|
function isAllowedWithPermission(Permission memory permission, uint256 handle) public view returns (bool) {
|
|
591
646
|
return acl.isAllowedWithPermission(permission, handle);
|
|
592
647
|
}
|
|
648
|
+
|
|
649
|
+
// Stub implementations for new ITaskManager interface methods (inc PR #48)
|
|
650
|
+
|
|
651
|
+
function createRandomTask(uint8 returnType, uint256 seed, int32 securityZone) external returns (uint256) {
|
|
652
|
+
// Mock implementation: just return a pseudo-random hash based on seed
|
|
653
|
+
return uint256(keccak256(abi.encode(returnType, seed, securityZone, block.timestamp)));
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
function isPubliclyAllowed(uint256 ctHash) external view returns (bool) {
|
|
657
|
+
revert NotImplemented();
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function verifyDecryptResult(uint256 ctHash, uint256 result, bytes calldata signature) external view returns (bool) {
|
|
661
|
+
// Mock implementation: verify signature using the verifier signer
|
|
662
|
+
bytes32 digest = keccak256(abi.encodePacked(result));
|
|
663
|
+
return ECDSA.recover(digest, signature) == verifierSigner;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function verifyDecryptResultSafe(
|
|
667
|
+
uint256 ctHash,
|
|
668
|
+
uint256 result,
|
|
669
|
+
bytes calldata signature
|
|
670
|
+
) external view returns (bool) {
|
|
671
|
+
// Same as verifyDecryptResult for mock
|
|
672
|
+
try this.verifyDecryptResult(ctHash, result, signature) returns (bool valid) {
|
|
673
|
+
return valid;
|
|
674
|
+
} catch {
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
593
678
|
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
2
2
|
// solhint-disable one-contract-per-file
|
|
3
3
|
|
|
4
|
+
// NOTE: This file was renamed from `MockQueryDecrypter.sol` to `MockThresholdNetwork.sol`
|
|
5
|
+
// to better reflect that it mocks threshold-network-style decryption/sealing behavior
|
|
6
|
+
// in local/testing environments.
|
|
7
|
+
|
|
4
8
|
pragma solidity >=0.8.19 <0.9.0;
|
|
5
9
|
|
|
6
10
|
import { MockACL } from './MockACL.sol';
|
|
7
11
|
import { MockTaskManager } from './MockTaskManager.sol';
|
|
8
12
|
import { Permission, MockPermissioned } from './Permissioned.sol';
|
|
9
13
|
|
|
10
|
-
contract
|
|
14
|
+
contract MockThresholdNetwork {
|
|
11
15
|
MockTaskManager public mockTaskManager;
|
|
12
16
|
MockACL public mockAcl;
|
|
13
17
|
|
|
@@ -111,6 +115,68 @@ contract MockQueryDecrypter {
|
|
|
111
115
|
return (true, '', seal(value, permission.sealingKey));
|
|
112
116
|
}
|
|
113
117
|
|
|
118
|
+
// DECRYPT FOR TX
|
|
119
|
+
|
|
120
|
+
function _isAllowedWithPermit(
|
|
121
|
+
uint256 ctHash,
|
|
122
|
+
Permission memory permission
|
|
123
|
+
) internal view returns (bool isAllowed, string memory error) {
|
|
124
|
+
try mockTaskManager.isAllowedWithPermission(permission, ctHash) returns (bool _isAllowed) {
|
|
125
|
+
isAllowed = _isAllowed;
|
|
126
|
+
} catch Error(string memory reason) {
|
|
127
|
+
return (false, reason);
|
|
128
|
+
} catch Panic(uint /*errorCode*/) {
|
|
129
|
+
return (false, 'Panic');
|
|
130
|
+
} catch (bytes memory lowLevelData) {
|
|
131
|
+
return (false, decodeLowLevelReversion(lowLevelData));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!isAllowed) return (false, 'NotAllowed');
|
|
135
|
+
return (true, '');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function _isAllowedWithoutPermit(uint256 ctHash) internal view returns (bool isAllowed, string memory error) {
|
|
139
|
+
try mockAcl.globalAllowed(ctHash) returns (bool _isAllowed) {
|
|
140
|
+
isAllowed = _isAllowed;
|
|
141
|
+
} catch Error(string memory reason) {
|
|
142
|
+
return (false, reason);
|
|
143
|
+
} catch Panic(uint /*errorCode*/) {
|
|
144
|
+
return (false, 'Panic');
|
|
145
|
+
} catch (bytes memory lowLevelData) {
|
|
146
|
+
return (false, decodeLowLevelReversion(lowLevelData));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!isAllowed) return (false, 'NotAllowed');
|
|
150
|
+
return (true, '');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// @notice Decrypt a ciphertext for a transaction using a permit.
|
|
154
|
+
function decryptForTxWithPermit(
|
|
155
|
+
uint256 ctHash,
|
|
156
|
+
Permission memory permission
|
|
157
|
+
) public view returns (bool allowed, string memory error, uint256 decryptedValue) {
|
|
158
|
+
if (permission.issuer == address(0)) {
|
|
159
|
+
return (false, 'PermissionMissing', 0);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
(bool isAllowed, string memory err) = _isAllowedWithPermit(ctHash, permission);
|
|
163
|
+
if (!isAllowed) return (false, err, 0);
|
|
164
|
+
|
|
165
|
+
uint256 value = mockTaskManager.mockStorage(ctHash);
|
|
166
|
+
return (true, '', value);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/// @notice Decrypt a ciphertext for a transaction using global allowance (no permit).
|
|
170
|
+
function decryptForTxWithoutPermit(
|
|
171
|
+
uint256 ctHash
|
|
172
|
+
) public view returns (bool allowed, string memory error, uint256 decryptedValue) {
|
|
173
|
+
(bool isAllowed, string memory err) = _isAllowedWithoutPermit(ctHash);
|
|
174
|
+
if (!isAllowed) return (false, err, 0);
|
|
175
|
+
|
|
176
|
+
uint256 value = mockTaskManager.mockStorage(ctHash);
|
|
177
|
+
return (true, '', value);
|
|
178
|
+
}
|
|
179
|
+
|
|
114
180
|
// UTIL
|
|
115
181
|
|
|
116
182
|
function decodeLowLevelReversion(bytes memory data) public pure returns (string memory error) {
|
package/contracts/TestBed.sol
CHANGED
|
@@ -3,14 +3,31 @@ pragma solidity ^0.8.13;
|
|
|
3
3
|
|
|
4
4
|
import '@fhenixprotocol/cofhe-contracts/FHE.sol';
|
|
5
5
|
|
|
6
|
+
/// @title TestBed
|
|
7
|
+
/// @notice Minimal contract used to smoke-test CoFHE/FHE flows (mocks + permissions + decrypt plumbing).
|
|
8
|
+
/// @dev This contract is intentionally simple and is primarily a convenience for Hardhat-based local
|
|
9
|
+
/// development: the Hardhat plugin can deploy it automatically when `deployTestBed` is enabled.
|
|
10
|
+
///
|
|
11
|
+
/// In Foundry flows, nothing deploys or depends on this contract automatically — tests must
|
|
12
|
+
/// instantiate it explicitly (this repository includes such Foundry tests). You may deploy/use it
|
|
13
|
+
/// in `forge test` if you want a known-good reference target.
|
|
14
|
+
///
|
|
15
|
+
/// Important concepts:
|
|
16
|
+
/// - `eNumber` is an on-chain encrypted handle type (`euint32`). In real CoFHE, this represents a ciphertext.
|
|
17
|
+
/// - `numberHash` stores the unwrapped handle (`ctHash`) as a uint256 for easy inspection/assertions.
|
|
18
|
+
/// - After every state update we call `FHE.allowThis(...)` and `FHE.allowSender(...)` so the contract
|
|
19
|
+
/// and the transaction sender can continue operating on / decrypting the updated handle.
|
|
6
20
|
contract TestBed {
|
|
7
21
|
euint32 public eNumber;
|
|
8
|
-
|
|
22
|
+
bytes32 public numberHash;
|
|
9
23
|
|
|
24
|
+
/// @notice Marker used by deploy scripts/tests to confirm the contract is deployed.
|
|
10
25
|
function exists() public pure returns (bool) {
|
|
11
26
|
return true;
|
|
12
27
|
}
|
|
13
28
|
|
|
29
|
+
/// @notice Sets `eNumber` from an encrypted input struct.
|
|
30
|
+
/// @dev Typically used when testing client-side encryption flows.
|
|
14
31
|
function setNumber(InEuint32 memory inNumber) public {
|
|
15
32
|
eNumber = FHE.asEuint32(inNumber);
|
|
16
33
|
numberHash = euint32.unwrap(eNumber);
|
|
@@ -18,19 +35,23 @@ contract TestBed {
|
|
|
18
35
|
FHE.allowSender(eNumber);
|
|
19
36
|
}
|
|
20
37
|
|
|
21
|
-
|
|
38
|
+
/// @notice Convenience setter that casts a plaintext value into an encrypted handle.
|
|
39
|
+
/// @dev Useful for quick smoke tests that don't need pre-encryption.
|
|
40
|
+
function setNumberTrivial(uint32 inNumber) public {
|
|
22
41
|
eNumber = FHE.asEuint32(inNumber);
|
|
23
42
|
numberHash = euint32.unwrap(eNumber);
|
|
24
43
|
FHE.allowThis(eNumber);
|
|
25
44
|
FHE.allowSender(eNumber);
|
|
26
45
|
}
|
|
27
46
|
|
|
47
|
+
/// @notice Increments `eNumber` by 1 using FHE arithmetic.
|
|
28
48
|
function increment() public {
|
|
29
49
|
eNumber = FHE.add(eNumber, FHE.asEuint32(1));
|
|
30
50
|
FHE.allowThis(eNumber);
|
|
31
51
|
FHE.allowSender(eNumber);
|
|
32
52
|
}
|
|
33
53
|
|
|
54
|
+
/// @notice Adds an encrypted input to `eNumber`.
|
|
34
55
|
function add(InEuint32 memory inNumber) public {
|
|
35
56
|
eNumber = FHE.add(eNumber, FHE.asEuint32(inNumber));
|
|
36
57
|
|
|
@@ -38,6 +59,7 @@ contract TestBed {
|
|
|
38
59
|
FHE.allowSender(eNumber);
|
|
39
60
|
}
|
|
40
61
|
|
|
62
|
+
/// @notice Subtracts an encrypted input from `eNumber`, clamped to 0 to avoid underflow.
|
|
41
63
|
function sub(InEuint32 memory inNumber) public {
|
|
42
64
|
euint32 inAsEuint32 = FHE.asEuint32(inNumber);
|
|
43
65
|
euint32 eSubOrZero = FHE.select(FHE.lte(inAsEuint32, eNumber), inAsEuint32, FHE.asEuint32(0));
|
|
@@ -46,21 +68,31 @@ contract TestBed {
|
|
|
46
68
|
FHE.allowSender(eNumber);
|
|
47
69
|
}
|
|
48
70
|
|
|
71
|
+
/// @notice Multiplies `eNumber` by an encrypted input.
|
|
49
72
|
function mul(InEuint32 memory inNumber) public {
|
|
50
73
|
eNumber = FHE.mul(eNumber, FHE.asEuint32(inNumber));
|
|
51
74
|
FHE.allowThis(eNumber);
|
|
52
75
|
FHE.allowSender(eNumber);
|
|
53
76
|
}
|
|
54
77
|
|
|
78
|
+
/// @notice Requests decryption of `eNumber`.
|
|
79
|
+
/// @dev In real CoFHE this is asynchronous; in mocks it is simulated.
|
|
55
80
|
function decrypt() public {
|
|
56
81
|
FHE.decrypt(eNumber);
|
|
57
82
|
}
|
|
58
83
|
|
|
84
|
+
/// @notice Reads a decryption result (reverts if not ready depending on implementation).
|
|
59
85
|
function getDecryptResult(euint32 input1) public view returns (uint32) {
|
|
60
86
|
return FHE.getDecryptResult(input1);
|
|
61
87
|
}
|
|
62
88
|
|
|
89
|
+
/// @notice Reads a decryption result safely, returning a readiness flag.
|
|
63
90
|
function getDecryptResultSafe(euint32 input1) public view returns (uint32 value, bool decrypted) {
|
|
64
91
|
return FHE.getDecryptResultSafe(input1);
|
|
65
92
|
}
|
|
93
|
+
|
|
94
|
+
/// @notice Publishes a decrypt result for an encrypted handle.
|
|
95
|
+
function publishDecryptResult(euint32 input, uint32 result, bytes memory signature) external {
|
|
96
|
+
FHE.publishDecryptResult(input, result, signature);
|
|
97
|
+
}
|
|
66
98
|
}
|
|
@@ -9,7 +9,7 @@ import { MockZkVerifier } from '../MockZkVerifier.sol';
|
|
|
9
9
|
import { MockZkVerifierSigner } from './MockZkVerifierSigner.sol';
|
|
10
10
|
import { MessageHashUtils } from '@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol';
|
|
11
11
|
import { Permission, PermissionUtils } from '../Permissioned.sol';
|
|
12
|
-
import {
|
|
12
|
+
import { MockThresholdNetwork } from '../MockThresholdNetwork.sol';
|
|
13
13
|
import { SIGNER_ADDRESS } from '../MockCoFHE.sol';
|
|
14
14
|
|
|
15
15
|
abstract contract CoFheTest is Test {
|
|
@@ -17,11 +17,13 @@ abstract contract CoFheTest is Test {
|
|
|
17
17
|
MockZkVerifier public mockZkVerifier;
|
|
18
18
|
MockZkVerifierSigner public mockZkVerifierSigner;
|
|
19
19
|
MockACL public mockAcl;
|
|
20
|
-
|
|
20
|
+
MockThresholdNetwork public mockThresholdNetwork;
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
address constant ZK_VERIFIER_ADDRESS =
|
|
24
|
-
address constant
|
|
22
|
+
// Keep these in sync with `packages/sdk/core/consts.ts`
|
|
23
|
+
address constant ZK_VERIFIER_ADDRESS = 0x0000000000000000000000000000000000005001;
|
|
24
|
+
address constant THRESHOLD_NETWORK_ADDRESS = 0x0000000000000000000000000000000000005002;
|
|
25
|
+
// SDK exposes this as `MOCKS_ZK_VERIFIER_SIGNER_ADDRESS`
|
|
26
|
+
address constant ZK_VERIFIER_SIGNER_ADDRESS = SIGNER_ADDRESS;
|
|
25
27
|
address public constant ACL_ADDRESS = 0xa6Ea4b5291d044D93b73b3CFf3109A1128663E8B;
|
|
26
28
|
|
|
27
29
|
bool private _log = false;
|
|
@@ -75,12 +77,12 @@ abstract contract CoFheTest is Test {
|
|
|
75
77
|
mockZkVerifierSigner = MockZkVerifierSigner(ZK_VERIFIER_SIGNER_ADDRESS);
|
|
76
78
|
vm.label(address(mockZkVerifierSigner), 'MockZkVerifierSigner');
|
|
77
79
|
|
|
78
|
-
//
|
|
80
|
+
// THRESHOLD NETWORK
|
|
79
81
|
|
|
80
|
-
deployCodeTo('
|
|
81
|
-
|
|
82
|
-
vm.label(address(
|
|
83
|
-
|
|
82
|
+
deployCodeTo('MockThresholdNetwork.sol:MockThresholdNetwork', THRESHOLD_NETWORK_ADDRESS);
|
|
83
|
+
mockThresholdNetwork = MockThresholdNetwork(THRESHOLD_NETWORK_ADDRESS);
|
|
84
|
+
vm.label(address(mockThresholdNetwork), 'MockThresholdNetwork');
|
|
85
|
+
mockThresholdNetwork.initialize(TASK_MANAGER_ADDRESS, address(mockAcl));
|
|
84
86
|
|
|
85
87
|
// SET LOG OPS
|
|
86
88
|
|
|
@@ -120,31 +122,46 @@ abstract contract CoFheTest is Test {
|
|
|
120
122
|
assertEq(mockTaskManager.inMockStorage(ctHash), true);
|
|
121
123
|
assertEq(mockTaskManager.mockStorage(ctHash), value);
|
|
122
124
|
}
|
|
125
|
+
|
|
126
|
+
function assertHashValue(bytes32 ctHash, uint256 value) public view {
|
|
127
|
+
assertHashValue(uint256(ctHash), value);
|
|
128
|
+
}
|
|
129
|
+
|
|
123
130
|
function assertHashValue(uint256 ctHash, uint256 value, string memory message) public view {
|
|
124
131
|
assertEq(mockTaskManager.inMockStorage(ctHash), true, message);
|
|
125
132
|
assertEq(mockTaskManager.mockStorage(ctHash), value, message);
|
|
126
133
|
}
|
|
127
134
|
|
|
135
|
+
function assertHashValue(bytes32 ctHash, uint256 value, string memory message) public view {
|
|
136
|
+
assertHashValue(uint256(ctHash), value, message);
|
|
137
|
+
}
|
|
138
|
+
|
|
128
139
|
// Encrypted types (no message)
|
|
129
140
|
|
|
130
141
|
function assertHashValue(ebool eValue, bool value) public view {
|
|
131
142
|
assertHashValue(ebool.unwrap(eValue), value ? 1 : 0);
|
|
132
143
|
}
|
|
144
|
+
|
|
133
145
|
function assertHashValue(euint8 eValue, uint8 value) public view {
|
|
134
146
|
assertHashValue(euint8.unwrap(eValue), value);
|
|
135
147
|
}
|
|
148
|
+
|
|
136
149
|
function assertHashValue(euint16 eValue, uint16 value) public view {
|
|
137
150
|
assertHashValue(euint16.unwrap(eValue), value);
|
|
138
151
|
}
|
|
152
|
+
|
|
139
153
|
function assertHashValue(euint32 eValue, uint32 value) public view {
|
|
140
154
|
assertHashValue(euint32.unwrap(eValue), value);
|
|
141
155
|
}
|
|
156
|
+
|
|
142
157
|
function assertHashValue(euint64 eValue, uint64 value) public view {
|
|
143
158
|
assertHashValue(euint64.unwrap(eValue), value);
|
|
144
159
|
}
|
|
160
|
+
|
|
145
161
|
function assertHashValue(euint128 eValue, uint128 value) public view {
|
|
146
162
|
assertHashValue(euint128.unwrap(eValue), value);
|
|
147
163
|
}
|
|
164
|
+
|
|
148
165
|
function assertHashValue(eaddress eValue, address value) public view {
|
|
149
166
|
assertHashValue(eaddress.unwrap(eValue), uint256(uint160(value)));
|
|
150
167
|
}
|
|
@@ -154,21 +171,27 @@ abstract contract CoFheTest is Test {
|
|
|
154
171
|
function assertHashValue(ebool eValue, bool value, string memory message) public view {
|
|
155
172
|
assertHashValue(ebool.unwrap(eValue), value ? 1 : 0, message);
|
|
156
173
|
}
|
|
174
|
+
|
|
157
175
|
function assertHashValue(euint8 eValue, uint8 value, string memory message) public view {
|
|
158
176
|
assertHashValue(euint8.unwrap(eValue), value, message);
|
|
159
177
|
}
|
|
178
|
+
|
|
160
179
|
function assertHashValue(euint16 eValue, uint16 value, string memory message) public view {
|
|
161
180
|
assertHashValue(euint16.unwrap(eValue), value, message);
|
|
162
181
|
}
|
|
182
|
+
|
|
163
183
|
function assertHashValue(euint32 eValue, uint32 value, string memory message) public view {
|
|
164
184
|
assertHashValue(euint32.unwrap(eValue), value, message);
|
|
165
185
|
}
|
|
186
|
+
|
|
166
187
|
function assertHashValue(euint64 eValue, uint64 value, string memory message) public view {
|
|
167
188
|
assertHashValue(euint64.unwrap(eValue), value, message);
|
|
168
189
|
}
|
|
190
|
+
|
|
169
191
|
function assertHashValue(euint128 eValue, uint128 value, string memory message) public view {
|
|
170
192
|
assertHashValue(euint128.unwrap(eValue), value, message);
|
|
171
193
|
}
|
|
194
|
+
|
|
172
195
|
function assertHashValue(eaddress eValue, address value, string memory message) public view {
|
|
173
196
|
assertHashValue(eaddress.unwrap(eValue), uint256(uint160(value)), message);
|
|
174
197
|
}
|
|
@@ -258,16 +281,6 @@ abstract contract CoFheTest is Test {
|
|
|
258
281
|
return abi.decode(abi.encode(createEncryptedInput(Utils.EUINT128_TFHE, value, securityZone, sender)), (InEuint128));
|
|
259
282
|
}
|
|
260
283
|
|
|
261
|
-
/**
|
|
262
|
-
* @notice Creates an InEuint256 to be used as FHE input. Value is stored in MockCoFHE contract, hash is a pointer to that value.
|
|
263
|
-
* @param value Value to encrypt.
|
|
264
|
-
* @param securityZone Security zone of the encrypted value.
|
|
265
|
-
* @return InEuint256.
|
|
266
|
-
*/
|
|
267
|
-
function createInEuint256(uint256 value, uint8 securityZone, address sender) public returns (InEuint256 memory) {
|
|
268
|
-
return abi.decode(abi.encode(createEncryptedInput(Utils.EUINT256_TFHE, value, securityZone, sender)), (InEuint256));
|
|
269
|
-
}
|
|
270
|
-
|
|
271
284
|
/**
|
|
272
285
|
* @notice Creates an InEaddress to be used as FHE input. Value is stored in MockCoFHE contract, hash is a pointer to that value.
|
|
273
286
|
* @param value Value to encrypt.
|
|
@@ -308,10 +321,6 @@ abstract contract CoFheTest is Test {
|
|
|
308
321
|
return createInEuint128(value, 0, sender);
|
|
309
322
|
}
|
|
310
323
|
|
|
311
|
-
function createInEuint256(uint256 value, address sender) public returns (InEuint256 memory) {
|
|
312
|
-
return createInEuint256(value, 0, sender);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
324
|
function createInEaddress(address value, address sender) public returns (InEaddress memory) {
|
|
316
325
|
return createInEaddress(value, 0, sender);
|
|
317
326
|
}
|
|
@@ -415,7 +424,7 @@ abstract contract CoFheTest is Test {
|
|
|
415
424
|
uint256 hostChainId,
|
|
416
425
|
Permission memory permission
|
|
417
426
|
) public view returns (bool, string memory error, uint256) {
|
|
418
|
-
return
|
|
427
|
+
return mockThresholdNetwork.queryDecrypt(ctHash, hostChainId, permission);
|
|
419
428
|
}
|
|
420
429
|
|
|
421
430
|
function querySealOutput(
|
|
@@ -423,10 +432,21 @@ abstract contract CoFheTest is Test {
|
|
|
423
432
|
uint256 hostChainId,
|
|
424
433
|
Permission memory permission
|
|
425
434
|
) public view returns (bool, string memory error, bytes32) {
|
|
426
|
-
return
|
|
435
|
+
return mockThresholdNetwork.querySealOutput(ctHash, hostChainId, permission);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function decryptForTxWithoutPermit(uint256 ctHash) public view returns (bool, string memory error, uint256) {
|
|
439
|
+
return mockThresholdNetwork.decryptForTxWithoutPermit(ctHash);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function decryptForTxWithPermit(
|
|
443
|
+
uint256 ctHash,
|
|
444
|
+
Permission memory permission
|
|
445
|
+
) public view returns (bool, string memory error, uint256) {
|
|
446
|
+
return mockThresholdNetwork.decryptForTxWithPermit(ctHash, permission);
|
|
427
447
|
}
|
|
428
448
|
|
|
429
449
|
function unseal(bytes32 hashed, bytes32 key) public view returns (uint256) {
|
|
430
|
-
return
|
|
450
|
+
return mockThresholdNetwork.unseal(hashed, key);
|
|
431
451
|
}
|
|
432
452
|
}
|