@iexec-nox/nox-protocol-contracts 0.1.0 → 0.2.1
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/contracts/NoxCompute.sol +904 -0
- package/contracts/sdk/Nox.sol +1 -102
- package/contracts/shared/TypeUtils.sol +13 -0
- package/package.json +2 -1
|
@@ -0,0 +1,904 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.27;
|
|
3
|
+
|
|
4
|
+
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
|
|
5
|
+
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
6
|
+
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
|
|
7
|
+
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
8
|
+
import {INoxCompute} from "./interfaces/INoxCompute.sol";
|
|
9
|
+
import {HandleUtils} from "./shared/HandleUtils.sol";
|
|
10
|
+
import {TEEType, TypeUtils} from "./shared/TypeUtils.sol";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @title NoxCompute
|
|
14
|
+
* This contract is the main entry point to the TEE compute functionalities of the Nox protocol.
|
|
15
|
+
* It's role includes:
|
|
16
|
+
* - Managing the access control list (ACL) for encrypted handles
|
|
17
|
+
* - Validating handle proofs issued by a trusted gateway
|
|
18
|
+
* - Facilitating the conversion of plaintext values to encrypted values
|
|
19
|
+
* - Triggering off-chain TEE computations through event emissions
|
|
20
|
+
*
|
|
21
|
+
* @dev Using non upgradeable EIP712 is safe here because it has no storage and the config is saved
|
|
22
|
+
* in immutable variables which should be enough here since we don't use multiple proxies with the
|
|
23
|
+
* same implementation.
|
|
24
|
+
*/
|
|
25
|
+
contract NoxCompute is INoxCompute, UUPSUpgradeable, OwnableUpgradeable, EIP712 {
|
|
26
|
+
/// @custom:storage-location erc7201:nox.storage.NoxCompute
|
|
27
|
+
struct NoxComputeStorage {
|
|
28
|
+
// An admin of a handle can:
|
|
29
|
+
// - use it as a computation input
|
|
30
|
+
// - decrypt its associated data off-chain
|
|
31
|
+
// - make it publicly decryptable
|
|
32
|
+
// - add other admins and viewers
|
|
33
|
+
mapping(bytes32 handleId => mapping(address => bool)) admins;
|
|
34
|
+
// A viewer of a handle can only decrypt its associated data off-chain.
|
|
35
|
+
//TODO: Make viewer expirable
|
|
36
|
+
mapping(bytes32 handleId => mapping(address => bool)) viewers;
|
|
37
|
+
// Handles that are publicly decryptable
|
|
38
|
+
mapping(bytes32 handle => bool) isPubliclyDecryptable;
|
|
39
|
+
bytes kmsPublicKey;
|
|
40
|
+
address gateway;
|
|
41
|
+
uint256 proofExpirationDuration;
|
|
42
|
+
// Counter used to guarantee handle uniqueness when all operands are public handles
|
|
43
|
+
uint256 uniqueSeedCounter;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
uint8 private constant HANDLE_VERSION = 0;
|
|
47
|
+
|
|
48
|
+
// keccak256(abi.encode(uint256(keccak256("nox.storage.NoxCompute")) - 1)) & ~bytes32(uint256(0xff))
|
|
49
|
+
bytes32 private constant NOX_COMPUTE_STORAGE_LOCATION =
|
|
50
|
+
0x118a408ef9c0c38d6620cca4d300c2ce1c4f4cbcd93520940a6461e96acdcd00;
|
|
51
|
+
bytes32 public constant HANDLE_PROOF_TYPEHASH = keccak256(
|
|
52
|
+
"HandleProof(bytes32 handle,address owner,address app,uint256 createdAt)"
|
|
53
|
+
);
|
|
54
|
+
bytes32 public constant DECRYPTION_PROOF_TYPEHASH = keccak256(
|
|
55
|
+
"DecryptionProof(bytes32 handle,bytes decryptedResult)"
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Ensures the account address is not zero
|
|
60
|
+
* @param account The address to validate
|
|
61
|
+
*/
|
|
62
|
+
modifier notZeroAddress(address account) {
|
|
63
|
+
require(account != address(0), InvalidZeroAddress());
|
|
64
|
+
_;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Ensures the sender is allowed to access the handle
|
|
69
|
+
* @param handle The handle to check access for
|
|
70
|
+
*/
|
|
71
|
+
modifier onlyAllowed(bytes32 handle) {
|
|
72
|
+
require(isAllowed(handle, msg.sender), UnauthorizedSender(msg.sender));
|
|
73
|
+
_;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Prevents ACL mutations on public handles.
|
|
78
|
+
* Applied before onlyAllowed to avoid unnecessary storage reads.
|
|
79
|
+
* @param handle The handle to check
|
|
80
|
+
*/
|
|
81
|
+
modifier notPublicHandle(bytes32 handle) {
|
|
82
|
+
require(!HandleUtils.isPublicHandle(handle), PublicHandleACLForbidden());
|
|
83
|
+
_;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @custom:oz-upgrades-unsafe-allow constructor
|
|
88
|
+
*/
|
|
89
|
+
constructor() EIP712("NoxCompute", "1") {
|
|
90
|
+
_disableInitializers();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Initializes the proxy contract state.
|
|
95
|
+
* @param initialOwner Initial owner address
|
|
96
|
+
* @param kmsPublicKey_ KMS public key for ECIES encryption
|
|
97
|
+
*/
|
|
98
|
+
function initialize(address initialOwner, bytes calldata kmsPublicKey_) public initializer {
|
|
99
|
+
require(kmsPublicKey_.length != 0, InvalidEmptyBytes());
|
|
100
|
+
__Ownable_init(initialOwner);
|
|
101
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
102
|
+
$.proofExpirationDuration = 1 hours;
|
|
103
|
+
$.kmsPublicKey = kmsPublicKey_;
|
|
104
|
+
_emitZeroHandleSeeds();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @notice Initializer of 0.1.1 upgrade for already deployed proxies.
|
|
109
|
+
* @notice Emits zero handle seeds for existing proxies.
|
|
110
|
+
* @dev The same logic is also called in `initialize()` for fresh deployments.
|
|
111
|
+
* @dev The call to this function does not need to be protected because it does
|
|
112
|
+
* not do any critical operations.
|
|
113
|
+
*/
|
|
114
|
+
function initializeV2() public reinitializer(2) {
|
|
115
|
+
_emitZeroHandleSeeds();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ----------- ACL management -----------
|
|
119
|
+
|
|
120
|
+
/// @inheritdoc INoxCompute
|
|
121
|
+
function allow(
|
|
122
|
+
bytes32 handle,
|
|
123
|
+
address account
|
|
124
|
+
) external override notZeroAddress(account) notPublicHandle(handle) onlyAllowed(handle) {
|
|
125
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
126
|
+
$.admins[handle][account] = true;
|
|
127
|
+
emit Allowed(msg.sender, account, handle);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @inheritdoc INoxCompute
|
|
132
|
+
* @dev To grant transient access, the caller must already have permission on `handle`.
|
|
133
|
+
* Transient access only lasts for the current transaction. It is the responsibility
|
|
134
|
+
* of the application contract to convert this into persistent access via `allow()`
|
|
135
|
+
* if needed.
|
|
136
|
+
*/
|
|
137
|
+
function allowTransient(
|
|
138
|
+
bytes32 handle,
|
|
139
|
+
address account
|
|
140
|
+
) external override notZeroAddress(account) notPublicHandle(handle) onlyAllowed(handle) {
|
|
141
|
+
_allowTransient(handle, account);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// @inheritdoc INoxCompute
|
|
145
|
+
function disallowTransient(
|
|
146
|
+
bytes32 handle,
|
|
147
|
+
address account
|
|
148
|
+
) external override notZeroAddress(account) notPublicHandle(handle) onlyAllowed(handle) {
|
|
149
|
+
bytes32 key = keccak256(abi.encodePacked(handle, account));
|
|
150
|
+
assembly {
|
|
151
|
+
tstore(key, 0)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// @inheritdoc INoxCompute
|
|
156
|
+
function isAllowed(bytes32 handle, address account) public view override returns (bool) {
|
|
157
|
+
return
|
|
158
|
+
HandleUtils.isPublicHandle(handle) ||
|
|
159
|
+
_isAllowedTransient(handle, account) ||
|
|
160
|
+
_isAllowedPersistent(handle, account);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/// @inheritdoc INoxCompute
|
|
164
|
+
function validateAllowedForAll(address account, bytes32[] memory handles) public view override {
|
|
165
|
+
for (uint256 i = 0; i < handles.length; i++) {
|
|
166
|
+
if (!isAllowed(handles[i], account)) {
|
|
167
|
+
revert NotAllowed(handles[i], account);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/// @inheritdoc INoxCompute
|
|
173
|
+
function addViewer(
|
|
174
|
+
bytes32 handle,
|
|
175
|
+
address viewer
|
|
176
|
+
) external override notZeroAddress(viewer) notPublicHandle(handle) onlyAllowed(handle) {
|
|
177
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
178
|
+
$.viewers[handle][viewer] = true;
|
|
179
|
+
emit ViewerAdded(msg.sender, viewer, handle);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// @inheritdoc INoxCompute
|
|
183
|
+
function isViewer(bytes32 handle, address viewer) external view override returns (bool) {
|
|
184
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
185
|
+
return
|
|
186
|
+
HandleUtils.isPublicHandle(handle) ||
|
|
187
|
+
$.isPubliclyDecryptable[handle] ||
|
|
188
|
+
$.viewers[handle][viewer] ||
|
|
189
|
+
$.admins[handle][viewer];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// @inheritdoc INoxCompute
|
|
193
|
+
function allowPublicDecryption(
|
|
194
|
+
bytes32 handle
|
|
195
|
+
) external override notPublicHandle(handle) onlyAllowed(handle) {
|
|
196
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
197
|
+
$.isPubliclyDecryptable[handle] = true;
|
|
198
|
+
emit MarkedAsPubliclyDecryptable(msg.sender, handle);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// @inheritdoc INoxCompute
|
|
202
|
+
function isPubliclyDecryptable(bytes32 handle) external view override returns (bool) {
|
|
203
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
204
|
+
return HandleUtils.isPublicHandle(handle) || $.isPubliclyDecryptable[handle];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Grants transient access to a handle for an account. This function does not do any
|
|
209
|
+
* checks and should be used with caution.
|
|
210
|
+
* This function is used in two scenarios:
|
|
211
|
+
* - For handles generated off-chain by the Handle Gateway, once the proof has been verified
|
|
212
|
+
* - For handles resulting from on-chain operations, where the caller naturally inherits
|
|
213
|
+
* rights on the output handle
|
|
214
|
+
* @param handle Handle identifier
|
|
215
|
+
* @param account Address of the account
|
|
216
|
+
*/
|
|
217
|
+
function _allowTransient(bytes32 handle, address account) private {
|
|
218
|
+
// Public handles don't need ACL; skip silently to save gas.
|
|
219
|
+
if (HandleUtils.isPublicHandle(handle)) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
bytes32 key = keccak256(abi.encodePacked(handle, account));
|
|
223
|
+
assembly {
|
|
224
|
+
tstore(key, 1)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Check if an address has transient access to handle.
|
|
230
|
+
* @param handle Handle.
|
|
231
|
+
* @param account Address of the account.
|
|
232
|
+
* @return Returns `true` if the address has transient access to a handle and `false` otherwise.
|
|
233
|
+
*/
|
|
234
|
+
function _isAllowedTransient(bytes32 handle, address account) private view returns (bool) {
|
|
235
|
+
bool isAllowedTransient_;
|
|
236
|
+
bytes32 key = keccak256(abi.encodePacked(handle, account));
|
|
237
|
+
assembly {
|
|
238
|
+
isAllowedTransient_ := tload(key)
|
|
239
|
+
}
|
|
240
|
+
return isAllowedTransient_;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Check if an address has persistent access to handle.
|
|
245
|
+
* @param handle Handle.
|
|
246
|
+
* @param account Address of the account.
|
|
247
|
+
* @return Returns `true` if the address has persistent access to a handle and `false` otherwise.
|
|
248
|
+
*/
|
|
249
|
+
function _isAllowedPersistent(bytes32 handle, address account) private view returns (bool) {
|
|
250
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
251
|
+
return $.admins[handle][account];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ----------- Compute functions -----------
|
|
255
|
+
|
|
256
|
+
/// @inheritdoc INoxCompute
|
|
257
|
+
function wrapAsPublicHandle(bytes32 value, TEEType teeType) external returns (bytes32 result) {
|
|
258
|
+
bytes32[] memory operands = new bytes32[](1);
|
|
259
|
+
operands[0] = value;
|
|
260
|
+
// Deterministic handle: same (value, type) always produces the same handle
|
|
261
|
+
result = _generatePublicHandle(Operator.WrapAsPublicHandle, operands, teeType);
|
|
262
|
+
_allowTransient(result, msg.sender);
|
|
263
|
+
emit WrapAsPublicHandle(msg.sender, value, teeType, result);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/// @inheritdoc INoxCompute
|
|
267
|
+
function validateInputProof(
|
|
268
|
+
bytes32 handle,
|
|
269
|
+
address owner,
|
|
270
|
+
bytes calldata proof,
|
|
271
|
+
TEEType teeType
|
|
272
|
+
) public {
|
|
273
|
+
bytes4 chainIdInHandle = bytes4(handle << (1 * 8));
|
|
274
|
+
require(
|
|
275
|
+
chainIdInHandle == bytes4(uint32(block.chainid)),
|
|
276
|
+
InvalidProof(proof, "Handle chain id mismatch")
|
|
277
|
+
);
|
|
278
|
+
require(TypeUtils.typeOf(handle) == teeType, InvalidProof(proof, "Handle type mismatch"));
|
|
279
|
+
require(proof.length == 137, InvalidProof(proof, "Invalid proof length"));
|
|
280
|
+
address ownerInProof;
|
|
281
|
+
address appInProof;
|
|
282
|
+
uint256 createdAt;
|
|
283
|
+
assembly {
|
|
284
|
+
ownerInProof := shr(96, calldataload(proof.offset))
|
|
285
|
+
appInProof := shr(96, calldataload(add(proof.offset, 20)))
|
|
286
|
+
createdAt := calldataload(add(proof.offset, 40))
|
|
287
|
+
}
|
|
288
|
+
bytes calldata signature = proof[72:137];
|
|
289
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
290
|
+
require(
|
|
291
|
+
block.timestamp <= createdAt + $.proofExpirationDuration,
|
|
292
|
+
InvalidProof(proof, "Proof expired")
|
|
293
|
+
);
|
|
294
|
+
require(appInProof == msg.sender, InvalidProof(proof, "App mismatch"));
|
|
295
|
+
require(ownerInProof == owner, InvalidProof(proof, "Owner mismatch"));
|
|
296
|
+
bytes32 eip712MessageHash = _hashTypedDataV4(
|
|
297
|
+
keccak256(
|
|
298
|
+
abi.encode(HANDLE_PROOF_TYPEHASH, handle, ownerInProof, appInProof, createdAt)
|
|
299
|
+
)
|
|
300
|
+
);
|
|
301
|
+
require(
|
|
302
|
+
ECDSA.recover(eip712MessageHash, signature) == $.gateway,
|
|
303
|
+
InvalidProof(proof, "Invalid signature")
|
|
304
|
+
);
|
|
305
|
+
// Give caller contract transient access to the handle.
|
|
306
|
+
_allowTransient(handle, msg.sender);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/// @inheritdoc INoxCompute
|
|
310
|
+
function validateDecryptionProof(
|
|
311
|
+
bytes32 handle,
|
|
312
|
+
bytes calldata decryptionProof
|
|
313
|
+
) external view returns (bytes memory) {
|
|
314
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
315
|
+
require(decryptionProof.length >= 65, InvalidProof(decryptionProof, "Proof too short"));
|
|
316
|
+
bytes calldata decryptedResult = decryptionProof[65:];
|
|
317
|
+
bytes32 eip712MessageHash = _hashTypedDataV4(
|
|
318
|
+
keccak256(abi.encode(DECRYPTION_PROOF_TYPEHASH, handle, keccak256(decryptedResult)))
|
|
319
|
+
);
|
|
320
|
+
require(
|
|
321
|
+
ECDSA.recoverCalldata(eip712MessageHash, decryptionProof[:65]) == $.gateway,
|
|
322
|
+
InvalidProof(decryptionProof, "Invalid signature")
|
|
323
|
+
);
|
|
324
|
+
return decryptedResult;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/// @inheritdoc INoxCompute
|
|
328
|
+
function add(
|
|
329
|
+
bytes32 leftHandOperand,
|
|
330
|
+
bytes32 rightHandOperand
|
|
331
|
+
) external returns (bytes32 result) {
|
|
332
|
+
bytes32[] memory operands = new bytes32[](2);
|
|
333
|
+
operands[0] = leftHandOperand;
|
|
334
|
+
operands[1] = rightHandOperand;
|
|
335
|
+
(, result) = _executeArithmeticOperation(Operator.Add, operands, false);
|
|
336
|
+
emit Add(msg.sender, leftHandOperand, rightHandOperand, result);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/// @inheritdoc INoxCompute
|
|
340
|
+
function sub(
|
|
341
|
+
bytes32 leftHandOperand,
|
|
342
|
+
bytes32 rightHandOperand
|
|
343
|
+
) external returns (bytes32 result) {
|
|
344
|
+
bytes32[] memory operands = new bytes32[](2);
|
|
345
|
+
operands[0] = leftHandOperand;
|
|
346
|
+
operands[1] = rightHandOperand;
|
|
347
|
+
(, result) = _executeArithmeticOperation(Operator.Sub, operands, false);
|
|
348
|
+
emit Sub(msg.sender, leftHandOperand, rightHandOperand, result);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/// @inheritdoc INoxCompute
|
|
352
|
+
function div(bytes32 numerator, bytes32 denominator) external returns (bytes32 result) {
|
|
353
|
+
bytes32[] memory operands = new bytes32[](2);
|
|
354
|
+
operands[0] = numerator;
|
|
355
|
+
operands[1] = denominator;
|
|
356
|
+
(, result) = _executeArithmeticOperation(Operator.Div, operands, false);
|
|
357
|
+
emit Div(msg.sender, numerator, denominator, result);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/// @inheritdoc INoxCompute
|
|
361
|
+
function mul(
|
|
362
|
+
bytes32 leftHandOperand,
|
|
363
|
+
bytes32 rightHandOperand
|
|
364
|
+
) external returns (bytes32 result) {
|
|
365
|
+
bytes32[] memory operands = new bytes32[](2);
|
|
366
|
+
operands[0] = leftHandOperand;
|
|
367
|
+
operands[1] = rightHandOperand;
|
|
368
|
+
(, result) = _executeArithmeticOperation(Operator.Mul, operands, false);
|
|
369
|
+
emit Mul(msg.sender, leftHandOperand, rightHandOperand, result);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/// @inheritdoc INoxCompute
|
|
373
|
+
function eq(
|
|
374
|
+
bytes32 leftHandOperand,
|
|
375
|
+
bytes32 rightHandOperand
|
|
376
|
+
) external returns (bytes32 result) {
|
|
377
|
+
result = _executeComparisonOperation(Operator.Eq, leftHandOperand, rightHandOperand);
|
|
378
|
+
emit Eq(msg.sender, leftHandOperand, rightHandOperand, result);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/// @inheritdoc INoxCompute
|
|
382
|
+
function ne(
|
|
383
|
+
bytes32 leftHandOperand,
|
|
384
|
+
bytes32 rightHandOperand
|
|
385
|
+
) external returns (bytes32 result) {
|
|
386
|
+
result = _executeComparisonOperation(Operator.Ne, leftHandOperand, rightHandOperand);
|
|
387
|
+
emit Ne(msg.sender, leftHandOperand, rightHandOperand, result);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/// @inheritdoc INoxCompute
|
|
391
|
+
function lt(
|
|
392
|
+
bytes32 leftHandOperand,
|
|
393
|
+
bytes32 rightHandOperand
|
|
394
|
+
) external returns (bytes32 result) {
|
|
395
|
+
result = _executeComparisonOperation(Operator.Lt, leftHandOperand, rightHandOperand);
|
|
396
|
+
emit Lt(msg.sender, leftHandOperand, rightHandOperand, result);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/// @inheritdoc INoxCompute
|
|
400
|
+
function le(
|
|
401
|
+
bytes32 leftHandOperand,
|
|
402
|
+
bytes32 rightHandOperand
|
|
403
|
+
) external returns (bytes32 result) {
|
|
404
|
+
result = _executeComparisonOperation(Operator.Le, leftHandOperand, rightHandOperand);
|
|
405
|
+
emit Le(msg.sender, leftHandOperand, rightHandOperand, result);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/// @inheritdoc INoxCompute
|
|
409
|
+
function gt(
|
|
410
|
+
bytes32 leftHandOperand,
|
|
411
|
+
bytes32 rightHandOperand
|
|
412
|
+
) external returns (bytes32 result) {
|
|
413
|
+
result = _executeComparisonOperation(Operator.Gt, leftHandOperand, rightHandOperand);
|
|
414
|
+
emit Gt(msg.sender, leftHandOperand, rightHandOperand, result);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/// @inheritdoc INoxCompute
|
|
418
|
+
function ge(
|
|
419
|
+
bytes32 leftHandOperand,
|
|
420
|
+
bytes32 rightHandOperand
|
|
421
|
+
) external returns (bytes32 result) {
|
|
422
|
+
result = _executeComparisonOperation(Operator.Ge, leftHandOperand, rightHandOperand);
|
|
423
|
+
emit Ge(msg.sender, leftHandOperand, rightHandOperand, result);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/// @inheritdoc INoxCompute
|
|
427
|
+
function safeAdd(
|
|
428
|
+
bytes32 leftHandOperand,
|
|
429
|
+
bytes32 rightHandOperand
|
|
430
|
+
) external returns (bytes32 success, bytes32 result) {
|
|
431
|
+
bytes32[] memory operands = new bytes32[](2);
|
|
432
|
+
operands[0] = leftHandOperand;
|
|
433
|
+
operands[1] = rightHandOperand;
|
|
434
|
+
(success, result) = _executeArithmeticOperation(Operator.SafeAdd, operands, true);
|
|
435
|
+
emit SafeAdd(msg.sender, leftHandOperand, rightHandOperand, success, result);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/// @inheritdoc INoxCompute
|
|
439
|
+
function safeSub(
|
|
440
|
+
bytes32 leftHandOperand,
|
|
441
|
+
bytes32 rightHandOperand
|
|
442
|
+
) external returns (bytes32 success, bytes32 result) {
|
|
443
|
+
bytes32[] memory operands = new bytes32[](2);
|
|
444
|
+
operands[0] = leftHandOperand;
|
|
445
|
+
operands[1] = rightHandOperand;
|
|
446
|
+
(success, result) = _executeArithmeticOperation(Operator.SafeSub, operands, true);
|
|
447
|
+
emit SafeSub(msg.sender, leftHandOperand, rightHandOperand, success, result);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/// @inheritdoc INoxCompute
|
|
451
|
+
function safeMul(
|
|
452
|
+
bytes32 leftHandOperand,
|
|
453
|
+
bytes32 rightHandOperand
|
|
454
|
+
) external returns (bytes32 success, bytes32 result) {
|
|
455
|
+
bytes32[] memory operands = new bytes32[](2);
|
|
456
|
+
operands[0] = leftHandOperand;
|
|
457
|
+
operands[1] = rightHandOperand;
|
|
458
|
+
(success, result) = _executeArithmeticOperation(Operator.SafeMul, operands, true);
|
|
459
|
+
emit SafeMul(msg.sender, leftHandOperand, rightHandOperand, success, result);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/// @inheritdoc INoxCompute
|
|
463
|
+
function safeDiv(
|
|
464
|
+
bytes32 numerator,
|
|
465
|
+
bytes32 denominator
|
|
466
|
+
) external returns (bytes32 success, bytes32 result) {
|
|
467
|
+
bytes32[] memory operands = new bytes32[](2);
|
|
468
|
+
operands[0] = numerator;
|
|
469
|
+
operands[1] = denominator;
|
|
470
|
+
(success, result) = _executeArithmeticOperation(Operator.SafeDiv, operands, true);
|
|
471
|
+
emit SafeDiv(msg.sender, numerator, denominator, success, result);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/// @inheritdoc INoxCompute
|
|
475
|
+
function select(
|
|
476
|
+
bytes32 condition,
|
|
477
|
+
bytes32 ifTrue,
|
|
478
|
+
bytes32 ifFalse
|
|
479
|
+
) external returns (bytes32 result) {
|
|
480
|
+
require(
|
|
481
|
+
condition != bytes32(0) && ifTrue != bytes32(0) && ifFalse != bytes32(0),
|
|
482
|
+
UndefinedHandle()
|
|
483
|
+
);
|
|
484
|
+
require(TypeUtils.typeOf(condition) == TEEType.Bool, UnsupportedType());
|
|
485
|
+
TEEType resultType = TypeUtils.typeOf(ifTrue);
|
|
486
|
+
require(resultType == TypeUtils.typeOf(ifFalse), IncompatibleTypes());
|
|
487
|
+
bytes32[] memory operands = new bytes32[](3);
|
|
488
|
+
operands[0] = condition;
|
|
489
|
+
operands[1] = ifTrue;
|
|
490
|
+
operands[2] = ifFalse;
|
|
491
|
+
validateAllowedForAll(msg.sender, operands);
|
|
492
|
+
result = _generateHandle(Operator.Select, operands, resultType);
|
|
493
|
+
_allowTransient(result, msg.sender);
|
|
494
|
+
emit Select(msg.sender, condition, ifTrue, ifFalse, result);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/// @inheritdoc INoxCompute
|
|
498
|
+
function transfer(
|
|
499
|
+
bytes32 balanceFrom,
|
|
500
|
+
bytes32 balanceTo,
|
|
501
|
+
bytes32 amount
|
|
502
|
+
) external returns (bytes32 success, bytes32 newBalanceFrom, bytes32 newBalanceTo) {
|
|
503
|
+
bytes32[] memory operands = new bytes32[](3);
|
|
504
|
+
operands[0] = balanceFrom;
|
|
505
|
+
operands[1] = balanceTo;
|
|
506
|
+
operands[2] = amount;
|
|
507
|
+
(success, newBalanceFrom, newBalanceTo) = _executeCompositeOperation(
|
|
508
|
+
Operator.Transfer,
|
|
509
|
+
operands
|
|
510
|
+
);
|
|
511
|
+
emit Transfer(
|
|
512
|
+
msg.sender,
|
|
513
|
+
balanceFrom,
|
|
514
|
+
balanceTo,
|
|
515
|
+
amount,
|
|
516
|
+
success,
|
|
517
|
+
newBalanceFrom,
|
|
518
|
+
newBalanceTo
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/// @inheritdoc INoxCompute
|
|
523
|
+
function mint(
|
|
524
|
+
bytes32 balanceTo,
|
|
525
|
+
bytes32 amount,
|
|
526
|
+
bytes32 totalSupply
|
|
527
|
+
) external returns (bytes32 success, bytes32 newBalanceTo, bytes32 newTotalSupply) {
|
|
528
|
+
bytes32[] memory operands = new bytes32[](3);
|
|
529
|
+
operands[0] = balanceTo;
|
|
530
|
+
operands[1] = amount;
|
|
531
|
+
operands[2] = totalSupply;
|
|
532
|
+
(success, newBalanceTo, newTotalSupply) = _executeCompositeOperation(
|
|
533
|
+
Operator.Mint,
|
|
534
|
+
operands
|
|
535
|
+
);
|
|
536
|
+
emit Mint(
|
|
537
|
+
msg.sender,
|
|
538
|
+
balanceTo,
|
|
539
|
+
amount,
|
|
540
|
+
totalSupply,
|
|
541
|
+
success,
|
|
542
|
+
newBalanceTo,
|
|
543
|
+
newTotalSupply
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/// @inheritdoc INoxCompute
|
|
548
|
+
function burn(
|
|
549
|
+
bytes32 balanceFrom,
|
|
550
|
+
bytes32 amount,
|
|
551
|
+
bytes32 totalSupply
|
|
552
|
+
) external returns (bytes32 success, bytes32 newBalanceFrom, bytes32 newTotalSupply) {
|
|
553
|
+
bytes32[] memory operands = new bytes32[](3);
|
|
554
|
+
operands[0] = balanceFrom;
|
|
555
|
+
operands[1] = amount;
|
|
556
|
+
operands[2] = totalSupply;
|
|
557
|
+
(success, newBalanceFrom, newTotalSupply) = _executeCompositeOperation(
|
|
558
|
+
Operator.Burn,
|
|
559
|
+
operands
|
|
560
|
+
);
|
|
561
|
+
emit Burn(
|
|
562
|
+
msg.sender,
|
|
563
|
+
balanceFrom,
|
|
564
|
+
amount,
|
|
565
|
+
totalSupply,
|
|
566
|
+
success,
|
|
567
|
+
newBalanceFrom,
|
|
568
|
+
newTotalSupply
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Executes an arithmetic operation on N encrypted handles.
|
|
574
|
+
* All operands must share the same type as the first operand, which also determines the result type.
|
|
575
|
+
* Verifies ACL permissions for all operands, checks type compatibility,
|
|
576
|
+
* generates result handle(s), and grants transient access to the caller.
|
|
577
|
+
*
|
|
578
|
+
* When `isSafeOperation` is true, generates an additional Bool success handle (outputIndex 1)
|
|
579
|
+
* and the result handle at outputIndex 0, enabling overflow/underflow detection.
|
|
580
|
+
*
|
|
581
|
+
* @dev Reverts with NotAllowed if caller lacks permission on any operand
|
|
582
|
+
* @dev Reverts with IncompatibleTypes if operand types don't match
|
|
583
|
+
*
|
|
584
|
+
* @param operator The operator to apply
|
|
585
|
+
* @param operands Array of operand handles
|
|
586
|
+
* @param isSafeOperation Whether to generate a Bool success handle alongside the result
|
|
587
|
+
* @return success The success flag handle (Bool type), bytes32(0) if not safe operation
|
|
588
|
+
* @return result The resulting encrypted handle
|
|
589
|
+
*/
|
|
590
|
+
function _executeArithmeticOperation(
|
|
591
|
+
Operator operator,
|
|
592
|
+
bytes32[] memory operands,
|
|
593
|
+
bool isSafeOperation
|
|
594
|
+
) private returns (bytes32 success, bytes32 result) {
|
|
595
|
+
_requireDefinedHandles(operands);
|
|
596
|
+
TEEType resultType = TypeUtils.typeOf(operands[0]);
|
|
597
|
+
TypeUtils.validateArithmeticType(resultType);
|
|
598
|
+
for (uint256 i = 1; i < operands.length; i++) {
|
|
599
|
+
if (resultType != TypeUtils.typeOf(operands[i])) {
|
|
600
|
+
revert IncompatibleTypes();
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
validateAllowedForAll(msg.sender, operands);
|
|
604
|
+
// Outputs differ by outputIndex and type, so they can safely share the same seed
|
|
605
|
+
uint256 uniqueSeed = _generateHandleUniqueSeed(operands);
|
|
606
|
+
result = _generateHandle(
|
|
607
|
+
operator,
|
|
608
|
+
operands,
|
|
609
|
+
resultType,
|
|
610
|
+
0,
|
|
611
|
+
uniqueSeed,
|
|
612
|
+
HandleUtils.ATTR_IS_UNIQUE_HANDLE
|
|
613
|
+
);
|
|
614
|
+
_allowTransient(result, msg.sender);
|
|
615
|
+
if (isSafeOperation) {
|
|
616
|
+
success = _generateHandle(
|
|
617
|
+
operator,
|
|
618
|
+
operands,
|
|
619
|
+
TEEType.Bool,
|
|
620
|
+
1,
|
|
621
|
+
uniqueSeed,
|
|
622
|
+
HandleUtils.ATTR_IS_UNIQUE_HANDLE
|
|
623
|
+
);
|
|
624
|
+
_allowTransient(success, msg.sender);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Executes a comparison operation on two encrypted handles.
|
|
630
|
+
* Both operands must share the same arithmetic type.
|
|
631
|
+
* Verifies ACL permissions for both operands, checks type compatibility,
|
|
632
|
+
* generates a Bool result handle, and grants transient access to the caller.
|
|
633
|
+
*
|
|
634
|
+
* @dev Reverts with NotAllowed if caller lacks permission on any operand
|
|
635
|
+
* @dev Reverts with IncompatibleTypes if operand types don't match
|
|
636
|
+
*
|
|
637
|
+
* @param operator The comparison operator to apply
|
|
638
|
+
* @param leftOperand Left-hand side operand handle
|
|
639
|
+
* @param rightOperand Right-hand side operand handle
|
|
640
|
+
* @return result The resulting Bool handle
|
|
641
|
+
*/
|
|
642
|
+
function _executeComparisonOperation(
|
|
643
|
+
Operator operator,
|
|
644
|
+
bytes32 leftOperand,
|
|
645
|
+
bytes32 rightOperand
|
|
646
|
+
) private returns (bytes32 result) {
|
|
647
|
+
require(leftOperand != bytes32(0) && rightOperand != bytes32(0), UndefinedHandle());
|
|
648
|
+
TEEType operandType = TypeUtils.typeOf(leftOperand);
|
|
649
|
+
TypeUtils.validateArithmeticType(operandType);
|
|
650
|
+
require(TypeUtils.typeOf(rightOperand) == operandType, IncompatibleTypes());
|
|
651
|
+
bytes32[] memory operands = new bytes32[](2);
|
|
652
|
+
operands[0] = leftOperand;
|
|
653
|
+
operands[1] = rightOperand;
|
|
654
|
+
validateAllowedForAll(msg.sender, operands);
|
|
655
|
+
result = _generateHandle(operator, operands, TEEType.Bool);
|
|
656
|
+
_allowTransient(result, msg.sender);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Executes a composite operation on 3 encrypted handles (e.g. transfer, mint, burn).
|
|
661
|
+
* All operands must share the same arithmetic type.
|
|
662
|
+
* Generates 3 output handles: a Bool success flag and two result handles of the input type.
|
|
663
|
+
*
|
|
664
|
+
* @param operator The operator to apply
|
|
665
|
+
* @param operands Array of 3 operand handles
|
|
666
|
+
* @return success The success flag handle (Bool type)
|
|
667
|
+
* @return result1 First result handle
|
|
668
|
+
* @return result2 Second result handle
|
|
669
|
+
*/
|
|
670
|
+
function _executeCompositeOperation(
|
|
671
|
+
Operator operator,
|
|
672
|
+
bytes32[] memory operands
|
|
673
|
+
) private returns (bytes32 success, bytes32 result1, bytes32 result2) {
|
|
674
|
+
_requireDefinedHandles(operands);
|
|
675
|
+
TEEType resultType = TypeUtils.typeOf(operands[0]);
|
|
676
|
+
TypeUtils.validateArithmeticType(resultType);
|
|
677
|
+
for (uint256 i = 1; i < operands.length; i++) {
|
|
678
|
+
if (resultType != TypeUtils.typeOf(operands[i])) {
|
|
679
|
+
revert IncompatibleTypes();
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
validateAllowedForAll(msg.sender, operands);
|
|
683
|
+
// Outputs differ by outputIndex and type, so they can safely share the same seed
|
|
684
|
+
uint256 uniqueSeed = _generateHandleUniqueSeed(operands);
|
|
685
|
+
success = _generateHandle(
|
|
686
|
+
operator,
|
|
687
|
+
operands,
|
|
688
|
+
TEEType.Bool,
|
|
689
|
+
0,
|
|
690
|
+
uniqueSeed,
|
|
691
|
+
HandleUtils.ATTR_IS_UNIQUE_HANDLE
|
|
692
|
+
);
|
|
693
|
+
result1 = _generateHandle(
|
|
694
|
+
operator,
|
|
695
|
+
operands,
|
|
696
|
+
resultType,
|
|
697
|
+
1,
|
|
698
|
+
uniqueSeed,
|
|
699
|
+
HandleUtils.ATTR_IS_UNIQUE_HANDLE
|
|
700
|
+
);
|
|
701
|
+
result2 = _generateHandle(
|
|
702
|
+
operator,
|
|
703
|
+
operands,
|
|
704
|
+
resultType,
|
|
705
|
+
2,
|
|
706
|
+
uniqueSeed,
|
|
707
|
+
HandleUtils.ATTR_IS_UNIQUE_HANDLE
|
|
708
|
+
);
|
|
709
|
+
_allowTransient(success, msg.sender);
|
|
710
|
+
_allowTransient(result1, msg.sender);
|
|
711
|
+
_allowTransient(result2, msg.sender);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Reverts if any operand is bytes32(0) (undefined handle).
|
|
716
|
+
*/
|
|
717
|
+
function _requireDefinedHandles(bytes32[] memory operands) private pure {
|
|
718
|
+
for (uint256 i = 0; i < operands.length; i++) {
|
|
719
|
+
require(operands[i] != bytes32(0), UndefinedHandle());
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* @dev Alias for _generateHandle producing a public handle (outputIndex=0, uniqueSeed=0, attrs=0x00).
|
|
725
|
+
*/
|
|
726
|
+
function _generatePublicHandle(
|
|
727
|
+
Operator operator,
|
|
728
|
+
bytes32[] memory operands,
|
|
729
|
+
TEEType handleType
|
|
730
|
+
) private view returns (bytes32 result) {
|
|
731
|
+
result = _generateHandle(operator, operands, handleType, 0, 0, bytes1(0x00));
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* @dev Alias for single-output confidential operations (outputIndex=0, attrs=ATTR_IS_UNIQUE_HANDLE).
|
|
736
|
+
* Computes the uniqueness seed internally.
|
|
737
|
+
* Must NOT be called multiple times for multi-output operations (the seed counter would diverge).
|
|
738
|
+
*/
|
|
739
|
+
function _generateHandle(
|
|
740
|
+
Operator operator,
|
|
741
|
+
bytes32[] memory operands,
|
|
742
|
+
TEEType handleType
|
|
743
|
+
) private returns (bytes32 result) {
|
|
744
|
+
uint256 uniqueSeed = _generateHandleUniqueSeed(operands);
|
|
745
|
+
result = _generateHandle(
|
|
746
|
+
operator,
|
|
747
|
+
operands,
|
|
748
|
+
handleType,
|
|
749
|
+
0,
|
|
750
|
+
uniqueSeed,
|
|
751
|
+
HandleUtils.ATTR_IS_UNIQUE_HANDLE
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Generates a complete handle from an operator and its operands.
|
|
757
|
+
*
|
|
758
|
+
* Pre-handle format:
|
|
759
|
+
* keccak256(abi.encode(
|
|
760
|
+
* operator, // Operator identifier (e.g., Add, Sub, WrapAsPublicHandle)
|
|
761
|
+
* operands, // Array of operand handles (or plaintext value)
|
|
762
|
+
* address(this), // NoxCompute contract address
|
|
763
|
+
* uniqueSeed, // Uniqueness seed (0 or counter value)
|
|
764
|
+
* outputIndex // For operations that return multiple outputs
|
|
765
|
+
* ))
|
|
766
|
+
*
|
|
767
|
+
* Handle format (32 bytes):
|
|
768
|
+
* [0] : Handle version
|
|
769
|
+
* [1-4] : Chain ID (4 bytes, uint32)
|
|
770
|
+
* [5] : TEE type
|
|
771
|
+
* [6] : Attributes (bit 0 = isUniqueHandle)
|
|
772
|
+
* [7-31] : Truncated pre-handle hash (25 bytes)
|
|
773
|
+
*
|
|
774
|
+
* @param operator The operator to apply
|
|
775
|
+
* @param operands Array of operand handles
|
|
776
|
+
* @param handleType The TEE type to encode in the handle
|
|
777
|
+
* @param outputIndex Index for operations returning multiple outputs
|
|
778
|
+
* @param uniqueSeed Uniqueness seed (0 for wrapAsPublicHandle and unique operands)
|
|
779
|
+
* @param attrs Attributes byte (0x00 for public handle, 0x01 for confidential)
|
|
780
|
+
* @return result The complete handle with metadata appended
|
|
781
|
+
*/
|
|
782
|
+
function _generateHandle(
|
|
783
|
+
Operator operator,
|
|
784
|
+
bytes32[] memory operands,
|
|
785
|
+
TEEType handleType,
|
|
786
|
+
uint8 outputIndex,
|
|
787
|
+
uint256 uniqueSeed,
|
|
788
|
+
bytes1 attrs
|
|
789
|
+
) private view returns (bytes32 result) {
|
|
790
|
+
result = keccak256(abi.encode(operator, operands, address(this), uniqueSeed, outputIndex));
|
|
791
|
+
// Shift hash to bytes 7-31 (truncate to 25 bytes), leaving bytes 0-6 free for metadata.
|
|
792
|
+
result = result >> (7 * 8);
|
|
793
|
+
result = result | bytes32(bytes1(uint8(HANDLE_VERSION)));
|
|
794
|
+
result = result | (bytes32(bytes4(uint32(block.chainid))) >> (1 * 8));
|
|
795
|
+
result = result | (bytes32(bytes1(uint8(handleType))) >> (5 * 8));
|
|
796
|
+
result = result | (bytes32(attrs) >> (6 * 8));
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Determines the uniqueness seed for a confidential operation.
|
|
801
|
+
* If at least one operand has isUniqueHandle=1, returns 0 (no storage access needed).
|
|
802
|
+
* If all operands are public handles, increments a storage counter to guarantee uniqueness.
|
|
803
|
+
* @param operands Array of operand handles
|
|
804
|
+
* @return The uniqueness seed
|
|
805
|
+
*/
|
|
806
|
+
function _generateHandleUniqueSeed(bytes32[] memory operands) private returns (uint256) {
|
|
807
|
+
for (uint256 i = 0; i < operands.length; i++) {
|
|
808
|
+
if (!HandleUtils.isPublicHandle(operands[i])) {
|
|
809
|
+
return 0;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
// All operands are public handles: need storage counter for uniqueness
|
|
813
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
814
|
+
return ++$.uniqueSeedCounter;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Emits events to seed the zero handles for all supported types. This allows off-chain
|
|
819
|
+
* services to recognize the zero handle for each type without needing to hardcode them.
|
|
820
|
+
*/
|
|
821
|
+
function _emitZeroHandleSeeds() private {
|
|
822
|
+
TEEType[] memory types = TypeUtils.allCurrentlySupportedTypes();
|
|
823
|
+
for (uint i = 0; i < types.length; i++) {
|
|
824
|
+
emit WrapAsPublicHandle(
|
|
825
|
+
address(this),
|
|
826
|
+
bytes32(0),
|
|
827
|
+
types[i],
|
|
828
|
+
HandleUtils.zeroHandle(types[i])
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// ----------- Admin functions ----------
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Sets the KMS public key used for ECIES encryption.
|
|
837
|
+
* Only callable by the owner.
|
|
838
|
+
* @param newKmsPublicKey Compressed SEC1 secp256k1 public key (33 bytes)
|
|
839
|
+
*/
|
|
840
|
+
function setKmsPublicKey(bytes calldata newKmsPublicKey) external onlyOwner {
|
|
841
|
+
require(newKmsPublicKey.length != 0, InvalidEmptyBytes());
|
|
842
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
843
|
+
$.kmsPublicKey = newKmsPublicKey;
|
|
844
|
+
emit KmsPublicKeyUpdated(newKmsPublicKey);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* Sets Gateway wallet address.
|
|
849
|
+
* Only callable by the owner.
|
|
850
|
+
* @param gatewayAddress New Gateway wallet address
|
|
851
|
+
*/
|
|
852
|
+
function setGateway(address gatewayAddress) external onlyOwner {
|
|
853
|
+
require(gatewayAddress != address(0), InvalidZeroAddress());
|
|
854
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
855
|
+
$.gateway = gatewayAddress;
|
|
856
|
+
emit GatewayUpdated(gatewayAddress);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* Sets the proof expiration duration.
|
|
861
|
+
* Only callable by the owner.
|
|
862
|
+
* @param newDuration New expiration duration in seconds
|
|
863
|
+
*/
|
|
864
|
+
function setProofExpirationDuration(uint256 newDuration) external onlyOwner {
|
|
865
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
866
|
+
$.proofExpirationDuration = newDuration;
|
|
867
|
+
emit ProofExpirationDurationUpdated(newDuration);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* Returns the KMS public key used for ECIES encryption.
|
|
872
|
+
*/
|
|
873
|
+
function kmsPublicKey() external view returns (bytes memory) {
|
|
874
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
875
|
+
return $.kmsPublicKey;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Returns the Gateway wallet address.
|
|
880
|
+
*/
|
|
881
|
+
function gateway() external view returns (address) {
|
|
882
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
883
|
+
return $.gateway;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Returns the proof expiration duration in seconds.
|
|
888
|
+
*/
|
|
889
|
+
function proofExpirationDuration() external view returns (uint256) {
|
|
890
|
+
NoxComputeStorage storage $ = _getNoxComputeStorage();
|
|
891
|
+
return $.proofExpirationDuration;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Authorizes contract upgrades only by the owner.
|
|
896
|
+
*/
|
|
897
|
+
function _authorizeUpgrade(address /*newImplementation*/) internal override onlyOwner {}
|
|
898
|
+
|
|
899
|
+
function _getNoxComputeStorage() internal pure returns (NoxComputeStorage storage $) {
|
|
900
|
+
assembly {
|
|
901
|
+
$.slot := NOX_COMPUTE_STORAGE_LOCATION
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
package/contracts/sdk/Nox.sol
CHANGED
|
@@ -34,7 +34,7 @@ library Nox {
|
|
|
34
34
|
}
|
|
35
35
|
// Local development chain
|
|
36
36
|
if (block.chainid == 31337) {
|
|
37
|
-
return
|
|
37
|
+
return 0x44C00793aD4975617b3B5Fc27D4FB78E772c8236;
|
|
38
38
|
}
|
|
39
39
|
revert("Nox: Unsupported chain");
|
|
40
40
|
}
|
|
@@ -85,16 +85,6 @@ library Nox {
|
|
|
85
85
|
return ebool.unwrap(handle) != 0;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
/**
|
|
89
|
-
* @dev Checks if an encrypted address handle is initialized.
|
|
90
|
-
* This is a basic check and does not guarantee that the handle
|
|
91
|
-
* is valid or recognized by the ACL.
|
|
92
|
-
* @param handle encrypted address handle
|
|
93
|
-
*/
|
|
94
|
-
function isInitialized(eaddress handle) internal pure returns (bool) {
|
|
95
|
-
return eaddress.unwrap(handle) != 0;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
88
|
/**
|
|
99
89
|
* @dev Checks if an encrypted uint16 handle is initialized.
|
|
100
90
|
* This is a basic check and does not guarantee that the handle
|
|
@@ -204,15 +194,6 @@ library Nox {
|
|
|
204
194
|
return ebool.wrap(handle);
|
|
205
195
|
}
|
|
206
196
|
|
|
207
|
-
function fromExternal(
|
|
208
|
-
externalEaddress externalHandle,
|
|
209
|
-
bytes calldata handleProof
|
|
210
|
-
) internal returns (eaddress) {
|
|
211
|
-
bytes32 handle = externalEaddress.unwrap(externalHandle);
|
|
212
|
-
_noxComputeContract().validateInputProof(handle, msg.sender, handleProof, TEEType.Address);
|
|
213
|
-
return eaddress.wrap(handle);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
197
|
function fromExternal(
|
|
217
198
|
externalEuint16 externalHandle,
|
|
218
199
|
bytes calldata handleProof
|
|
@@ -902,14 +883,6 @@ library Nox {
|
|
|
902
883
|
_allowIfNotPublic(ebool.unwrap(value), account);
|
|
903
884
|
}
|
|
904
885
|
|
|
905
|
-
/**
|
|
906
|
-
* @dev Allows the use of value for the address account.
|
|
907
|
-
* Silently skips public handles (they are already accessible by everyone).
|
|
908
|
-
*/
|
|
909
|
-
function allow(eaddress value, address account) internal {
|
|
910
|
-
_allowIfNotPublic(eaddress.unwrap(value), account);
|
|
911
|
-
}
|
|
912
|
-
|
|
913
886
|
/**
|
|
914
887
|
* @dev Allows the use of value for the address account.
|
|
915
888
|
* Silently skips public handles (they are already accessible by everyone).
|
|
@@ -950,14 +923,6 @@ library Nox {
|
|
|
950
923
|
_allowIfNotPublic(ebool.unwrap(value), address(this));
|
|
951
924
|
}
|
|
952
925
|
|
|
953
|
-
/**
|
|
954
|
-
* @dev Allows the use of value for this address (address(this)).
|
|
955
|
-
* Silently skips public handles (they are already accessible by everyone).
|
|
956
|
-
*/
|
|
957
|
-
function allowThis(eaddress value) internal {
|
|
958
|
-
_allowIfNotPublic(eaddress.unwrap(value), address(this));
|
|
959
|
-
}
|
|
960
|
-
|
|
961
926
|
/**
|
|
962
927
|
* @dev Allows the use of value for this address (address(this)).
|
|
963
928
|
* Silently skips public handles (they are already accessible by everyone).
|
|
@@ -998,14 +963,6 @@ library Nox {
|
|
|
998
963
|
_allowTransientIfNotPublic(ebool.unwrap(value), account);
|
|
999
964
|
}
|
|
1000
965
|
|
|
1001
|
-
/**
|
|
1002
|
-
* @dev Allows the use of value by address account for this transaction.
|
|
1003
|
-
* Silently skips public handles (they are already accessible by everyone).
|
|
1004
|
-
*/
|
|
1005
|
-
function allowTransient(eaddress value, address account) internal {
|
|
1006
|
-
_allowTransientIfNotPublic(eaddress.unwrap(value), account);
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
966
|
/**
|
|
1010
967
|
* @dev Allows the use of value by address account for this transaction.
|
|
1011
968
|
* Silently skips public handles (they are already accessible by everyone).
|
|
@@ -1046,14 +1003,6 @@ library Nox {
|
|
|
1046
1003
|
_disallowTransientIfNotPublic(ebool.unwrap(value), account);
|
|
1047
1004
|
}
|
|
1048
1005
|
|
|
1049
|
-
/**
|
|
1050
|
-
* @dev Revokes transient access to value for address account within the current transaction.
|
|
1051
|
-
* Silently skips public handles (they are already accessible by everyone).
|
|
1052
|
-
*/
|
|
1053
|
-
function disallowTransient(eaddress value, address account) internal {
|
|
1054
|
-
_disallowTransientIfNotPublic(eaddress.unwrap(value), account);
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
1006
|
/**
|
|
1058
1007
|
* @dev Revokes transient access to value for address account within the current transaction.
|
|
1059
1008
|
* Silently skips public handles (they are already accessible by everyone).
|
|
@@ -1093,13 +1042,6 @@ library Nox {
|
|
|
1093
1042
|
return _noxComputeContract().isAllowed(ebool.unwrap(handle), account);
|
|
1094
1043
|
}
|
|
1095
1044
|
|
|
1096
|
-
/**
|
|
1097
|
-
* @dev Checks if the handle is allowed for the account.
|
|
1098
|
-
*/
|
|
1099
|
-
function isAllowed(eaddress handle, address account) internal view returns (bool) {
|
|
1100
|
-
return _noxComputeContract().isAllowed(eaddress.unwrap(handle), account);
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
1045
|
/**
|
|
1104
1046
|
* @dev Checks if the handle is allowed for the account.
|
|
1105
1047
|
*/
|
|
@@ -1137,13 +1079,6 @@ library Nox {
|
|
|
1137
1079
|
_noxComputeContract().addViewer(ebool.unwrap(value), viewer);
|
|
1138
1080
|
}
|
|
1139
1081
|
|
|
1140
|
-
/**
|
|
1141
|
-
* @dev Adds a viewer for an eaddress handle.
|
|
1142
|
-
*/
|
|
1143
|
-
function addViewer(eaddress value, address viewer) internal {
|
|
1144
|
-
_noxComputeContract().addViewer(eaddress.unwrap(value), viewer);
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
1082
|
/**
|
|
1148
1083
|
* @dev Adds a viewer for an euint16 handle.
|
|
1149
1084
|
*/
|
|
@@ -1179,13 +1114,6 @@ library Nox {
|
|
|
1179
1114
|
return _noxComputeContract().isViewer(ebool.unwrap(handle), viewer);
|
|
1180
1115
|
}
|
|
1181
1116
|
|
|
1182
|
-
/**
|
|
1183
|
-
* @dev Checks if the viewer can view the handle.
|
|
1184
|
-
*/
|
|
1185
|
-
function isViewer(eaddress handle, address viewer) internal view returns (bool) {
|
|
1186
|
-
return _noxComputeContract().isViewer(eaddress.unwrap(handle), viewer);
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
1117
|
/**
|
|
1190
1118
|
* @dev Checks if the viewer can view the handle.
|
|
1191
1119
|
*/
|
|
@@ -1223,13 +1151,6 @@ library Nox {
|
|
|
1223
1151
|
_noxComputeContract().allowPublicDecryption(ebool.unwrap(value));
|
|
1224
1152
|
}
|
|
1225
1153
|
|
|
1226
|
-
/**
|
|
1227
|
-
* @dev Marks an eaddress handle as publicly decryptable.
|
|
1228
|
-
*/
|
|
1229
|
-
function allowPublicDecryption(eaddress value) internal {
|
|
1230
|
-
_noxComputeContract().allowPublicDecryption(eaddress.unwrap(value));
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
1154
|
/**
|
|
1234
1155
|
* @dev Marks an euint16 handle as publicly decryptable.
|
|
1235
1156
|
*/
|
|
@@ -1265,13 +1186,6 @@ library Nox {
|
|
|
1265
1186
|
return _noxComputeContract().isPubliclyDecryptable(ebool.unwrap(handle));
|
|
1266
1187
|
}
|
|
1267
1188
|
|
|
1268
|
-
/**
|
|
1269
|
-
* @dev Checks if the handle is publicly decryptable.
|
|
1270
|
-
*/
|
|
1271
|
-
function isPubliclyDecryptable(eaddress handle) internal view returns (bool) {
|
|
1272
|
-
return _noxComputeContract().isPubliclyDecryptable(eaddress.unwrap(handle));
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
1189
|
/**
|
|
1276
1190
|
* @dev Checks if the handle is publicly decryptable.
|
|
1277
1191
|
*/
|
|
@@ -1318,21 +1232,6 @@ library Nox {
|
|
|
1318
1232
|
return result[0] != 0x00;
|
|
1319
1233
|
}
|
|
1320
1234
|
|
|
1321
|
-
/**
|
|
1322
|
-
* @dev Verifies a decryption proof and returns the decrypted address value.
|
|
1323
|
-
*/
|
|
1324
|
-
function publicDecrypt(
|
|
1325
|
-
eaddress handle,
|
|
1326
|
-
bytes calldata decryptionProof
|
|
1327
|
-
) internal view returns (address plaintextValue) {
|
|
1328
|
-
bytes memory result = _noxComputeContract().validateDecryptionProof(
|
|
1329
|
-
eaddress.unwrap(handle),
|
|
1330
|
-
decryptionProof
|
|
1331
|
-
);
|
|
1332
|
-
require(result.length == 20, MalformedDecryptedData(result));
|
|
1333
|
-
return address(bytes20(result));
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
1235
|
/**
|
|
1337
1236
|
* @dev Verifies a decryption proof and returns the decrypted uint16 value.
|
|
1338
1237
|
*/
|
|
@@ -116,6 +116,19 @@ error NonArithmeticType();
|
|
|
116
116
|
error UnsupportedArithmeticType();
|
|
117
117
|
|
|
118
118
|
library TypeUtils {
|
|
119
|
+
/**
|
|
120
|
+
* Returns the list of all currently supported TEE types.
|
|
121
|
+
* @dev Update this list when new types are supported.
|
|
122
|
+
*/
|
|
123
|
+
function allCurrentlySupportedTypes() internal pure returns (TEEType[] memory types) {
|
|
124
|
+
types = new TEEType[](5);
|
|
125
|
+
types[0] = TEEType.Bool;
|
|
126
|
+
types[1] = TEEType.Uint16;
|
|
127
|
+
types[2] = TEEType.Uint256;
|
|
128
|
+
types[3] = TEEType.Int16;
|
|
129
|
+
types[4] = TEEType.Int256;
|
|
130
|
+
}
|
|
131
|
+
|
|
119
132
|
/**
|
|
120
133
|
* @notice Extracts the TEE type from a handle.
|
|
121
134
|
* The type is stored at byte position 5 in the handle.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iexec-nox/nox-protocol-contracts",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Nox protocol smart contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Nox",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"packageManager": "pnpm@10.33.0",
|
|
12
12
|
"type": "module",
|
|
13
13
|
"files": [
|
|
14
|
+
"/contracts/NoxCompute.sol",
|
|
14
15
|
"/contracts/interfaces/",
|
|
15
16
|
"/contracts/sdk/",
|
|
16
17
|
"/contracts/shared/"
|