@cofhe/mock-contracts 0.1.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/README.md +126 -0
- package/contracts/MockACL.sol +338 -0
- package/contracts/MockCoFHE.sol +441 -0
- package/contracts/MockQueryDecrypter.sol +133 -0
- package/contracts/MockTaskManager.sol +593 -0
- package/contracts/MockZkVerifier.sol +140 -0
- package/contracts/Permissioned.sol +213 -0
- package/contracts/TestBed.sol +66 -0
- package/contracts/foundry/CoFheTest.sol +432 -0
- package/contracts/foundry/MockZkVerifierSigner.sol +40 -0
- package/dist/index.d.mts +261 -0
- package/dist/index.d.ts +261 -0
- package/dist/index.js +2488 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2482 -0
- package/dist/index.mjs.map +1 -0
- package/foundry.toml +7 -0
- package/package.json +55 -0
- package/remappings.txt +5 -0
- package/test/TestBed.t.sol +60 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
2
|
+
// solhint-disable one-contract-per-file
|
|
3
|
+
|
|
4
|
+
pragma solidity >=0.8.19 <0.9.0;
|
|
5
|
+
|
|
6
|
+
import { TASK_MANAGER_ADDRESS } from '@fhenixprotocol/cofhe-contracts/FHE.sol';
|
|
7
|
+
import { EncryptedInput } from '@fhenixprotocol/cofhe-contracts/ICofhe.sol';
|
|
8
|
+
import { MockTaskManager } from './MockTaskManager.sol';
|
|
9
|
+
|
|
10
|
+
contract MockZkVerifier {
|
|
11
|
+
// TMCommon
|
|
12
|
+
uint256 constant hashMaskForMetadata = type(uint256).max - type(uint16).max; // 2 bytes reserved for metadata
|
|
13
|
+
uint256 constant uintTypeMask = (type(uint8).max >> 1); // 0x7f - 7 bits reserved for uint type in the one before last byte
|
|
14
|
+
uint256 constant triviallyEncryptedMask = type(uint8).max - uintTypeMask; //0x80 1 bit reserved for isTriviallyEncrypted
|
|
15
|
+
|
|
16
|
+
// Specific
|
|
17
|
+
uint256 salt = 0;
|
|
18
|
+
error InvalidInputs();
|
|
19
|
+
|
|
20
|
+
// EXISTENCE
|
|
21
|
+
|
|
22
|
+
function exists() public pure returns (bool) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// HASHING
|
|
27
|
+
|
|
28
|
+
function getByteForTrivialAndType(bool isTrivial, uint8 uintType) internal pure returns (uint256) {
|
|
29
|
+
/// @dev first bit for isTriviallyEncrypted
|
|
30
|
+
/// @dev last 7 bits for uintType
|
|
31
|
+
|
|
32
|
+
return uint256(((isTrivial ? triviallyEncryptedMask : 0x00) | (uintType & uintTypeMask)));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function _appendMetadata(
|
|
36
|
+
uint256 preCtHash,
|
|
37
|
+
uint8 securityZone,
|
|
38
|
+
uint8 uintType,
|
|
39
|
+
bool isTrivial
|
|
40
|
+
) internal pure returns (uint256 result) {
|
|
41
|
+
result = preCtHash & hashMaskForMetadata;
|
|
42
|
+
uint256 metadata = (getByteForTrivialAndType(isTrivial, uintType) << 8) | (uint256(uint8(int8(securityZone)))); /// @dev 8 bits for type, 8 bits for securityZone
|
|
43
|
+
result = result | metadata;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function uint256ToBytes32(uint256 value) internal pure returns (bytes memory) {
|
|
47
|
+
bytes memory result = new bytes(32);
|
|
48
|
+
assembly {
|
|
49
|
+
mstore(add(result, 32), value)
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function _calcPlaceholderKey(
|
|
55
|
+
address user,
|
|
56
|
+
uint8 utype,
|
|
57
|
+
uint8 securityZone,
|
|
58
|
+
uint256 input
|
|
59
|
+
) internal view returns (uint256) {
|
|
60
|
+
bytes memory combined = bytes.concat(uint256ToBytes32(input));
|
|
61
|
+
combined = bytes.concat(combined, uint256ToBytes32(uint256(uint160(user))));
|
|
62
|
+
combined = bytes.concat(combined, keccak256(abi.encodePacked(salt)));
|
|
63
|
+
|
|
64
|
+
// Calculate Keccak256 hash
|
|
65
|
+
bytes32 ctHash = keccak256(combined);
|
|
66
|
+
|
|
67
|
+
return _appendMetadata(uint256(ctHash), securityZone, utype, false);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// CORE
|
|
71
|
+
|
|
72
|
+
function zkVerifyCalcCtHashesPacked(
|
|
73
|
+
uint256[] memory values,
|
|
74
|
+
uint8[] memory utypes,
|
|
75
|
+
address user,
|
|
76
|
+
uint8 securityZone,
|
|
77
|
+
uint256 chainId
|
|
78
|
+
) public view returns (uint256[] memory ctHashes) {
|
|
79
|
+
if (utypes.length != values.length) {
|
|
80
|
+
revert InvalidInputs();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
ctHashes = new uint256[](utypes.length);
|
|
84
|
+
|
|
85
|
+
for (uint256 i = 0; i < utypes.length; i++) {
|
|
86
|
+
ctHashes[i] = zkVerifyCalcCtHash(values[i], utypes[i], user, securityZone, chainId);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function zkVerifyCalcCtHash(
|
|
91
|
+
uint256 value,
|
|
92
|
+
uint8 utype,
|
|
93
|
+
address user,
|
|
94
|
+
uint8 securityZone,
|
|
95
|
+
uint256
|
|
96
|
+
) public view returns (uint256 ctHash) {
|
|
97
|
+
ctHash = _calcPlaceholderKey(user, utype, securityZone, value);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function insertPackedCtHashes(uint256[] memory ctHashes, uint256[] memory values) public {
|
|
101
|
+
for (uint256 i = 0; i < ctHashes.length; i++) {
|
|
102
|
+
insertCtHash(ctHashes[i], values[i]);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function insertCtHash(uint256 ctHash, uint256 value) public {
|
|
107
|
+
MockTaskManager(TASK_MANAGER_ADDRESS).MOCK_setInEuintKey(ctHash, value);
|
|
108
|
+
salt += 1;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function zkVerifyPacked(
|
|
112
|
+
uint256[] memory values,
|
|
113
|
+
uint8[] memory utypes,
|
|
114
|
+
address user,
|
|
115
|
+
uint8 securityZone,
|
|
116
|
+
uint256 chainId
|
|
117
|
+
) public returns (EncryptedInput[] memory inputs) {
|
|
118
|
+
if (utypes.length != values.length) {
|
|
119
|
+
revert InvalidInputs();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
inputs = new EncryptedInput[](utypes.length);
|
|
123
|
+
|
|
124
|
+
for (uint256 i = 0; i < utypes.length; i++) {
|
|
125
|
+
inputs[i] = zkVerify(values[i], utypes[i], user, securityZone, chainId);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function zkVerify(
|
|
130
|
+
uint256 value,
|
|
131
|
+
uint8 utype,
|
|
132
|
+
address user,
|
|
133
|
+
uint8 securityZone,
|
|
134
|
+
uint256
|
|
135
|
+
) public returns (EncryptedInput memory) {
|
|
136
|
+
uint256 ctHash = _calcPlaceholderKey(user, utype, securityZone, value);
|
|
137
|
+
insertCtHash(ctHash, value);
|
|
138
|
+
return EncryptedInput({ ctHash: ctHash, securityZone: securityZone, utype: utype, signature: hex'' });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// solhint-disable func-name-mixedcase
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
pragma solidity >=0.8.19 <0.9.0;
|
|
4
|
+
|
|
5
|
+
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
|
|
6
|
+
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @dev Permission body that must be passed to a contract to allow access to sensitive data.
|
|
10
|
+
*
|
|
11
|
+
* The minimum permission to access a user's own data requires the fields
|
|
12
|
+
* < issuer, expiration, sealingKey, issuerSignature >
|
|
13
|
+
*
|
|
14
|
+
* ---
|
|
15
|
+
*
|
|
16
|
+
* If not sharing the permission, `issuer` signs a signature using the fields:
|
|
17
|
+
* < issuer, expiration, sealingKey, issuerSignature >
|
|
18
|
+
* This signature can now be used by `issuer` to access their own encrypted data.
|
|
19
|
+
*
|
|
20
|
+
* ---
|
|
21
|
+
*
|
|
22
|
+
* Sharing a permission is a two step process: `issuer` completes step 1, and `recipient` completes step 2.
|
|
23
|
+
*
|
|
24
|
+
* 1:
|
|
25
|
+
* `issuer` creates a permission with `recipient` populated with the address of the user to give access to.
|
|
26
|
+
* `issuer` does not include a `sealingKey` in the permission, it will be populated by the `recipient`.
|
|
27
|
+
* `issuer` signs a signature including the fields: (note: `sealingKey` is absent in this signature)
|
|
28
|
+
* < issuer, expiration, sealingKey, issuerSignature >
|
|
29
|
+
* `issuer` packages the permission data and `issuerSignature` and shares it with `recipient`
|
|
30
|
+
* ** None of this data is sensitive, and can be shared as cleartext **
|
|
31
|
+
*
|
|
32
|
+
* 2:
|
|
33
|
+
* `recipient` adds their `sealingKey` to the data received from `issuer`
|
|
34
|
+
* `recipient` signs a signature including the fields:
|
|
35
|
+
* < sealingKey, issuerSignature >
|
|
36
|
+
* `recipient` can now use the completed Permission to access `issuer`s encrypted data.
|
|
37
|
+
*
|
|
38
|
+
* ---
|
|
39
|
+
*
|
|
40
|
+
* `validatorId` and `validatorContract` are optional and can be used together to
|
|
41
|
+
* increase security and control by disabling a permission after it has been created.
|
|
42
|
+
* Useful when sharing permits to provide external access to sensitive data (eg auditors).
|
|
43
|
+
*/
|
|
44
|
+
struct Permission {
|
|
45
|
+
// (base) User that initially created the permission, target of data fetching
|
|
46
|
+
address issuer;
|
|
47
|
+
// (base) Expiration timestamp
|
|
48
|
+
uint64 expiration;
|
|
49
|
+
// (sharing) The user that this permission will be shared with
|
|
50
|
+
// ** optional, use `address(0)` to disable **
|
|
51
|
+
address recipient;
|
|
52
|
+
// (issuer defined validation) An id used to query a contract to check this permissions validity
|
|
53
|
+
// ** optional, use `0` to disable **
|
|
54
|
+
uint256 validatorId;
|
|
55
|
+
// (issuer defined validation) The contract to query to determine permission validity
|
|
56
|
+
// ** optional, user `address(0)` to disable **
|
|
57
|
+
address validatorContract;
|
|
58
|
+
// (base) The publicKey of a sealingPair used to re-encrypt `issuer`s confidential data
|
|
59
|
+
// (non-sharing) Populated by `issuer`
|
|
60
|
+
// (sharing) Populated by `recipient`
|
|
61
|
+
bytes32 sealingKey;
|
|
62
|
+
// (base) `signTypedData` signature created by `issuer`.
|
|
63
|
+
// (base) Shared- and Self- permissions differ in signature format: (`sealingKey` absent in shared signature)
|
|
64
|
+
// (non-sharing) < issuer, expiration, recipient, validatorId, validatorContract, sealingKey >
|
|
65
|
+
// (sharing) < issuer, expiration, recipient, validatorId, validatorContract >
|
|
66
|
+
bytes issuerSignature;
|
|
67
|
+
// (sharing) `signTypedData` signature created by `recipient` with format:
|
|
68
|
+
// (sharing) < sealingKey, issuerSignature>
|
|
69
|
+
// ** required for shared permits **
|
|
70
|
+
bytes recipientSignature;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// @dev Minimum required interface to create a custom permission validator.
|
|
74
|
+
/// Permission validators are optional, and provide extra security and control when sharing permits.
|
|
75
|
+
interface IPermissionCustomIdValidator {
|
|
76
|
+
/// @dev Checks whether a permission is valid, returning `false` disables the permission.
|
|
77
|
+
function disabled(address issuer, uint256 id) external view returns (bool);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
contract MockPermissioned is EIP712 {
|
|
81
|
+
using PermissionUtils for Permission;
|
|
82
|
+
|
|
83
|
+
constructor() EIP712("ACL", "1") {}
|
|
84
|
+
|
|
85
|
+
/// @dev Emitted when `permission.expiration` is in the past (< block.timestamp)
|
|
86
|
+
error PermissionInvalid_Expired();
|
|
87
|
+
|
|
88
|
+
/// @dev Emitted when `issuerSignature` is malformed or was not signed by `permission.issuer`
|
|
89
|
+
error PermissionInvalid_IssuerSignature();
|
|
90
|
+
|
|
91
|
+
/// @dev Emitted when `recipientSignature` is malformed or was not signed by `permission.recipient`
|
|
92
|
+
error PermissionInvalid_RecipientSignature();
|
|
93
|
+
|
|
94
|
+
/// @dev Emitted when `validatorContract` returned `false` indicating that this permission has been externally disabled
|
|
95
|
+
error PermissionInvalid_Disabled();
|
|
96
|
+
|
|
97
|
+
/// @dev Validate's a `permissions` access of sensitive data.
|
|
98
|
+
/// `permission` may be invalid or unauthorized for the following reasons:
|
|
99
|
+
/// - Expired: `permission.expiration` is in the past (< block.timestamp)
|
|
100
|
+
/// - Issuer signature: `issuerSignature` is malformed or was not signed by `permission.issuer`
|
|
101
|
+
/// - Recipient signature: `recipientSignature` is malformed or was not signed by `permission.recipient`
|
|
102
|
+
/// - Disabled: `validatorContract` returned `false` indicating that this permission has been externally disabled
|
|
103
|
+
/// @param permission Permission struct containing data necessary to validate data access and seal for return.
|
|
104
|
+
///
|
|
105
|
+
/// NOTE: Functions protected by `withPermission` should return ONLY the sensitive data of `permission.issuer`.
|
|
106
|
+
/// !! Returning data of `msg.sender` will leak sensitive values - `msg.sender` cannot be trusted in view functions !!
|
|
107
|
+
modifier withPermission(Permission memory permission) {
|
|
108
|
+
// Expiration
|
|
109
|
+
if (permission.expiration < block.timestamp)
|
|
110
|
+
revert PermissionInvalid_Expired();
|
|
111
|
+
|
|
112
|
+
// Issuer signature
|
|
113
|
+
if (
|
|
114
|
+
!SignatureChecker.isValidSignatureNow(
|
|
115
|
+
permission.issuer,
|
|
116
|
+
_hashTypedDataV4(permission.issuerHash()),
|
|
117
|
+
permission.issuerSignature
|
|
118
|
+
)
|
|
119
|
+
) revert PermissionInvalid_IssuerSignature();
|
|
120
|
+
|
|
121
|
+
// (if applicable) Recipient signature
|
|
122
|
+
if (
|
|
123
|
+
permission.recipient != address(0) &&
|
|
124
|
+
!SignatureChecker.isValidSignatureNow(
|
|
125
|
+
permission.recipient,
|
|
126
|
+
_hashTypedDataV4(permission.recipientHash()),
|
|
127
|
+
permission.recipientSignature
|
|
128
|
+
)
|
|
129
|
+
) revert PermissionInvalid_RecipientSignature();
|
|
130
|
+
|
|
131
|
+
// (if applicable) Externally disabled
|
|
132
|
+
if (
|
|
133
|
+
permission.validatorId != 0 &&
|
|
134
|
+
permission.validatorContract != address(0) &&
|
|
135
|
+
IPermissionCustomIdValidator(permission.validatorContract).disabled(
|
|
136
|
+
permission.issuer,
|
|
137
|
+
permission.validatorId
|
|
138
|
+
)
|
|
139
|
+
) revert PermissionInvalid_Disabled();
|
|
140
|
+
|
|
141
|
+
_;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function hashTypedDataV4(
|
|
145
|
+
bytes32 structHash
|
|
146
|
+
) public view virtual returns (bytes32) {
|
|
147
|
+
return _hashTypedDataV4(structHash);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/// @dev Internal utility library to improve the readability of PermissionedV2
|
|
152
|
+
/// Primarily focused on signature type hashes
|
|
153
|
+
library PermissionUtils {
|
|
154
|
+
function issuerHash(
|
|
155
|
+
Permission memory permission
|
|
156
|
+
) internal pure returns (bytes32) {
|
|
157
|
+
if (permission.recipient == address(0))
|
|
158
|
+
return issuerSelfHash(permission);
|
|
159
|
+
return issuerSharedHash(permission);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function issuerSelfHash(
|
|
163
|
+
Permission memory permission
|
|
164
|
+
) internal pure returns (bytes32) {
|
|
165
|
+
return
|
|
166
|
+
keccak256(
|
|
167
|
+
abi.encode(
|
|
168
|
+
keccak256(
|
|
169
|
+
"PermissionedV2IssuerSelf(address issuer,uint64 expiration,address recipient,uint256 validatorId,address validatorContract,bytes32 sealingKey)"
|
|
170
|
+
),
|
|
171
|
+
permission.issuer,
|
|
172
|
+
permission.expiration,
|
|
173
|
+
permission.recipient,
|
|
174
|
+
permission.validatorId,
|
|
175
|
+
permission.validatorContract,
|
|
176
|
+
permission.sealingKey
|
|
177
|
+
)
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function issuerSharedHash(
|
|
182
|
+
Permission memory permission
|
|
183
|
+
) internal pure returns (bytes32) {
|
|
184
|
+
return
|
|
185
|
+
keccak256(
|
|
186
|
+
abi.encode(
|
|
187
|
+
keccak256(
|
|
188
|
+
"PermissionedV2IssuerShared(address issuer,uint64 expiration,address recipient,uint256 validatorId,address validatorContract)"
|
|
189
|
+
),
|
|
190
|
+
permission.issuer,
|
|
191
|
+
permission.expiration,
|
|
192
|
+
permission.recipient,
|
|
193
|
+
permission.validatorId,
|
|
194
|
+
permission.validatorContract
|
|
195
|
+
)
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function recipientHash(
|
|
200
|
+
Permission memory permission
|
|
201
|
+
) internal pure returns (bytes32) {
|
|
202
|
+
return
|
|
203
|
+
keccak256(
|
|
204
|
+
abi.encode(
|
|
205
|
+
keccak256(
|
|
206
|
+
"PermissionedV2Recipient(bytes32 sealingKey,bytes issuerSignature)"
|
|
207
|
+
),
|
|
208
|
+
permission.sealingKey,
|
|
209
|
+
keccak256(permission.issuerSignature)
|
|
210
|
+
)
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// SPDX-License-Identifier: UNLICENSED
|
|
2
|
+
pragma solidity ^0.8.13;
|
|
3
|
+
|
|
4
|
+
import '@fhenixprotocol/cofhe-contracts/FHE.sol';
|
|
5
|
+
|
|
6
|
+
contract TestBed {
|
|
7
|
+
euint32 public eNumber;
|
|
8
|
+
uint256 public numberHash;
|
|
9
|
+
|
|
10
|
+
function exists() public pure returns (bool) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function setNumber(InEuint32 memory inNumber) public {
|
|
15
|
+
eNumber = FHE.asEuint32(inNumber);
|
|
16
|
+
numberHash = euint32.unwrap(eNumber);
|
|
17
|
+
FHE.allowThis(eNumber);
|
|
18
|
+
FHE.allowSender(eNumber);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function setNumberTrivial(uint256 inNumber) public {
|
|
22
|
+
eNumber = FHE.asEuint32(inNumber);
|
|
23
|
+
numberHash = euint32.unwrap(eNumber);
|
|
24
|
+
FHE.allowThis(eNumber);
|
|
25
|
+
FHE.allowSender(eNumber);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function increment() public {
|
|
29
|
+
eNumber = FHE.add(eNumber, FHE.asEuint32(1));
|
|
30
|
+
FHE.allowThis(eNumber);
|
|
31
|
+
FHE.allowSender(eNumber);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function add(InEuint32 memory inNumber) public {
|
|
35
|
+
eNumber = FHE.add(eNumber, FHE.asEuint32(inNumber));
|
|
36
|
+
|
|
37
|
+
FHE.allowThis(eNumber);
|
|
38
|
+
FHE.allowSender(eNumber);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function sub(InEuint32 memory inNumber) public {
|
|
42
|
+
euint32 inAsEuint32 = FHE.asEuint32(inNumber);
|
|
43
|
+
euint32 eSubOrZero = FHE.select(FHE.lte(inAsEuint32, eNumber), inAsEuint32, FHE.asEuint32(0));
|
|
44
|
+
eNumber = FHE.sub(eNumber, eSubOrZero);
|
|
45
|
+
FHE.allowThis(eNumber);
|
|
46
|
+
FHE.allowSender(eNumber);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function mul(InEuint32 memory inNumber) public {
|
|
50
|
+
eNumber = FHE.mul(eNumber, FHE.asEuint32(inNumber));
|
|
51
|
+
FHE.allowThis(eNumber);
|
|
52
|
+
FHE.allowSender(eNumber);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function decrypt() public {
|
|
56
|
+
FHE.decrypt(eNumber);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getDecryptResult(euint32 input1) public view returns (uint32) {
|
|
60
|
+
return FHE.getDecryptResult(input1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getDecryptResultSafe(euint32 input1) public view returns (uint32 value, bool decrypted) {
|
|
64
|
+
return FHE.getDecryptResultSafe(input1);
|
|
65
|
+
}
|
|
66
|
+
}
|