@iexec-nox/nox-protocol-contracts 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,655 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity ^0.8.35;
3
+
4
+ import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
5
+ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
6
+ import {HandleUtils} from "../utils/HandleUtils.sol";
7
+ import {TEEType, TypeUtils} from "../utils/TypeUtils.sol";
8
+ import {INoxCompute} from "../interfaces/INoxCompute.sol";
9
+ import {Common} from "./Common.sol";
10
+
11
+ /**
12
+ * @title Compute
13
+ * @notice TEE compute operations: handle wrapping, EIP712 proof validation,
14
+ * arithmetic, comparisons, optimized transfer/mint/burn.
15
+ *
16
+ * @dev Using non-upgradeable EIP712 is safe here as it has no storage and the config is saved
17
+ * in immutable variables, which is sufficient since we don't use multiple proxies with the
18
+ * same implementation.
19
+ */
20
+ abstract contract Compute is Common, EIP712 {
21
+ using TypeUtils for bytes32;
22
+
23
+ uint8 private constant HANDLE_VERSION = 0;
24
+
25
+ bytes32 public constant HANDLE_PROOF_TYPEHASH = keccak256(
26
+ "HandleProof(bytes32 handle,address owner,address app,uint256 createdAt)"
27
+ );
28
+ bytes32 public constant DECRYPTION_PROOF_TYPEHASH = keccak256(
29
+ "DecryptionProof(bytes32 handle,bytes decryptedResult)"
30
+ );
31
+
32
+ /// @inheritdoc INoxCompute
33
+ function wrapAsPublicHandle(
34
+ bytes32 value,
35
+ TEEType teeType
36
+ ) external override returns (bytes32 result) {
37
+ bytes32[] memory operands = new bytes32[](1);
38
+ operands[0] = value;
39
+ // Deterministic handle: same (value, type) always produces the same handle
40
+ // Generate a public handle (outputIndex=0, uniqueSeed=0, attrs=0x00)
41
+ result = _generateHandle(
42
+ Operator.WrapAsPublicHandle,
43
+ operands,
44
+ teeType,
45
+ 0,
46
+ 0,
47
+ bytes1(0x00)
48
+ );
49
+ _allowTransient(result, msg.sender);
50
+ emit WrapAsPublicHandle(msg.sender, value, teeType, result);
51
+ }
52
+
53
+ /// @inheritdoc INoxCompute
54
+ function validateInputProof(
55
+ bytes32 handle,
56
+ address owner,
57
+ bytes calldata proof,
58
+ TEEType teeType
59
+ ) external override {
60
+ bytes4 chainIdInHandle = bytes4(handle << (1 * 8));
61
+ require(
62
+ chainIdInHandle == bytes4(uint32(block.chainid)),
63
+ InvalidProof(proof, "Handle chain id mismatch")
64
+ );
65
+ require(TypeUtils.typeOf(handle) == teeType, InvalidProof(proof, "Handle type mismatch"));
66
+ require(proof.length == 137, InvalidProof(proof, "Invalid proof length"));
67
+ address ownerInProof;
68
+ address appInProof;
69
+ uint256 createdAt;
70
+ assembly {
71
+ ownerInProof := shr(96, calldataload(proof.offset))
72
+ appInProof := shr(96, calldataload(add(proof.offset, 20)))
73
+ createdAt := calldataload(add(proof.offset, 40))
74
+ }
75
+ bytes calldata signature = proof[72:137];
76
+ NoxComputeStorage storage $ = _getNoxComputeStorage();
77
+ require(
78
+ block.timestamp <= createdAt + $.proofExpirationDuration,
79
+ InvalidProof(proof, "Proof expired")
80
+ );
81
+ require(appInProof == msg.sender, InvalidProof(proof, "App mismatch"));
82
+ require(ownerInProof == owner, InvalidProof(proof, "Owner mismatch"));
83
+ bytes32 eip712MessageHash = _hashTypedDataV4(
84
+ keccak256(
85
+ abi.encode(HANDLE_PROOF_TYPEHASH, handle, ownerInProof, appInProof, createdAt)
86
+ )
87
+ );
88
+ require(
89
+ ECDSA.recover(eip712MessageHash, signature) == $.gateway,
90
+ InvalidProof(proof, "Invalid signature")
91
+ );
92
+ // Give caller contract transient access to the handle.
93
+ _allowTransient(handle, msg.sender);
94
+ }
95
+
96
+ /// @inheritdoc INoxCompute
97
+ function validateDecryptionProof(
98
+ bytes32 handle,
99
+ bytes calldata decryptionProof
100
+ ) external view override returns (bytes memory) {
101
+ NoxComputeStorage storage $ = _getNoxComputeStorage();
102
+ require(decryptionProof.length >= 65, InvalidProof(decryptionProof, "Proof too short"));
103
+ bytes calldata decryptedResult = decryptionProof[65:];
104
+ bytes32 eip712MessageHash = _hashTypedDataV4(
105
+ keccak256(abi.encode(DECRYPTION_PROOF_TYPEHASH, handle, keccak256(decryptedResult)))
106
+ );
107
+ require(
108
+ ECDSA.recoverCalldata(eip712MessageHash, decryptionProof[:65]) == $.gateway,
109
+ InvalidProof(decryptionProof, "Invalid signature")
110
+ );
111
+ return decryptedResult;
112
+ }
113
+
114
+ /// @inheritdoc INoxCompute
115
+ function add(
116
+ bytes32 leftHandOperand,
117
+ bytes32 rightHandOperand
118
+ ) external override returns (bytes32 result) {
119
+ TypeUtils.validateOperationTypes(leftHandOperand, rightHandOperand);
120
+ bytes32[] memory operands = new bytes32[](2);
121
+ operands[0] = leftHandOperand;
122
+ operands[1] = rightHandOperand;
123
+ (, bytes32[] memory results) = _executeOperation(
124
+ Operator.Add,
125
+ operands,
126
+ operands[0].typeOf(), // Result type
127
+ 1,
128
+ false
129
+ );
130
+ result = results[0];
131
+ emit Add(msg.sender, leftHandOperand, rightHandOperand, result);
132
+ }
133
+
134
+ /// @inheritdoc INoxCompute
135
+ function sub(
136
+ bytes32 leftHandOperand,
137
+ bytes32 rightHandOperand
138
+ ) external override returns (bytes32 result) {
139
+ TypeUtils.validateOperationTypes(leftHandOperand, rightHandOperand);
140
+ bytes32[] memory operands = new bytes32[](2);
141
+ operands[0] = leftHandOperand;
142
+ operands[1] = rightHandOperand;
143
+ (, bytes32[] memory results) = _executeOperation(
144
+ Operator.Sub,
145
+ operands,
146
+ operands[0].typeOf(), // Result type
147
+ 1,
148
+ false
149
+ );
150
+ result = results[0];
151
+ emit Sub(msg.sender, leftHandOperand, rightHandOperand, result);
152
+ }
153
+
154
+ /// @inheritdoc INoxCompute
155
+ function div(
156
+ bytes32 numerator,
157
+ bytes32 denominator
158
+ ) external override returns (bytes32 result) {
159
+ TypeUtils.validateOperationTypes(numerator, denominator);
160
+ bytes32[] memory operands = new bytes32[](2);
161
+ operands[0] = numerator;
162
+ operands[1] = denominator;
163
+ (, bytes32[] memory results) = _executeOperation(
164
+ Operator.Div,
165
+ operands,
166
+ operands[0].typeOf(), // Result type
167
+ 1,
168
+ false
169
+ );
170
+ result = results[0];
171
+ emit Div(msg.sender, numerator, denominator, result);
172
+ }
173
+
174
+ /// @inheritdoc INoxCompute
175
+ function mul(
176
+ bytes32 leftHandOperand,
177
+ bytes32 rightHandOperand
178
+ ) external override returns (bytes32 result) {
179
+ TypeUtils.validateOperationTypes(leftHandOperand, rightHandOperand);
180
+ bytes32[] memory operands = new bytes32[](2);
181
+ operands[0] = leftHandOperand;
182
+ operands[1] = rightHandOperand;
183
+ (, bytes32[] memory results) = _executeOperation(
184
+ Operator.Mul,
185
+ operands,
186
+ operands[0].typeOf(), // Result type
187
+ 1,
188
+ false
189
+ );
190
+ result = results[0];
191
+ emit Mul(msg.sender, leftHandOperand, rightHandOperand, result);
192
+ }
193
+
194
+ /// @inheritdoc INoxCompute
195
+ function safeAdd(
196
+ bytes32 leftHandOperand,
197
+ bytes32 rightHandOperand
198
+ ) external override returns (bytes32 success, bytes32 result) {
199
+ TypeUtils.validateOperationTypes(leftHandOperand, rightHandOperand);
200
+ bytes32[] memory operands = new bytes32[](2);
201
+ operands[0] = leftHandOperand;
202
+ operands[1] = rightHandOperand;
203
+ bytes32[] memory results;
204
+ (success, results) = _executeOperation(
205
+ Operator.SafeAdd,
206
+ operands,
207
+ operands[0].typeOf(), // Result type
208
+ 1,
209
+ true
210
+ );
211
+ result = results[0];
212
+ emit SafeAdd(msg.sender, leftHandOperand, rightHandOperand, success, result);
213
+ }
214
+
215
+ /// @inheritdoc INoxCompute
216
+ function safeSub(
217
+ bytes32 leftHandOperand,
218
+ bytes32 rightHandOperand
219
+ ) external override returns (bytes32 success, bytes32 result) {
220
+ TypeUtils.validateOperationTypes(leftHandOperand, rightHandOperand);
221
+ bytes32[] memory operands = new bytes32[](2);
222
+ operands[0] = leftHandOperand;
223
+ operands[1] = rightHandOperand;
224
+ bytes32[] memory results;
225
+ (success, results) = _executeOperation(
226
+ Operator.SafeSub,
227
+ operands,
228
+ operands[0].typeOf(), // Result type
229
+ 1,
230
+ true
231
+ );
232
+ result = results[0];
233
+ emit SafeSub(msg.sender, leftHandOperand, rightHandOperand, success, result);
234
+ }
235
+
236
+ /// @inheritdoc INoxCompute
237
+ function safeMul(
238
+ bytes32 leftHandOperand,
239
+ bytes32 rightHandOperand
240
+ ) external override returns (bytes32 success, bytes32 result) {
241
+ TypeUtils.validateOperationTypes(leftHandOperand, rightHandOperand);
242
+ bytes32[] memory operands = new bytes32[](2);
243
+ operands[0] = leftHandOperand;
244
+ operands[1] = rightHandOperand;
245
+ bytes32[] memory results;
246
+ (success, results) = _executeOperation(
247
+ Operator.SafeMul,
248
+ operands,
249
+ operands[0].typeOf(), // Result type
250
+ 1,
251
+ true
252
+ );
253
+ result = results[0];
254
+ emit SafeMul(msg.sender, leftHandOperand, rightHandOperand, success, result);
255
+ }
256
+
257
+ /// @inheritdoc INoxCompute
258
+ function safeDiv(
259
+ bytes32 numerator,
260
+ bytes32 denominator
261
+ ) external override returns (bytes32 success, bytes32 result) {
262
+ TypeUtils.validateOperationTypes(numerator, denominator);
263
+ bytes32[] memory operands = new bytes32[](2);
264
+ operands[0] = numerator;
265
+ operands[1] = denominator;
266
+ bytes32[] memory results;
267
+ (success, results) = _executeOperation(
268
+ Operator.SafeDiv,
269
+ operands,
270
+ operands[0].typeOf(), // Result type
271
+ 1,
272
+ true
273
+ );
274
+ result = results[0];
275
+ emit SafeDiv(msg.sender, numerator, denominator, success, result);
276
+ }
277
+
278
+ /// @inheritdoc INoxCompute
279
+ function eq(
280
+ bytes32 leftHandOperand,
281
+ bytes32 rightHandOperand
282
+ ) external override returns (bytes32 result) {
283
+ TypeUtils.validateOperationTypes(leftHandOperand, rightHandOperand);
284
+ bytes32[] memory operands = new bytes32[](2);
285
+ operands[0] = leftHandOperand;
286
+ operands[1] = rightHandOperand;
287
+ (, bytes32[] memory results) = _executeOperation(
288
+ Operator.Eq,
289
+ operands,
290
+ TEEType.Bool, // Result type
291
+ 1,
292
+ false
293
+ );
294
+ result = results[0];
295
+ emit Eq(msg.sender, leftHandOperand, rightHandOperand, result);
296
+ }
297
+
298
+ /// @inheritdoc INoxCompute
299
+ function ne(
300
+ bytes32 leftHandOperand,
301
+ bytes32 rightHandOperand
302
+ ) external override returns (bytes32 result) {
303
+ TypeUtils.validateOperationTypes(leftHandOperand, rightHandOperand);
304
+ bytes32[] memory operands = new bytes32[](2);
305
+ operands[0] = leftHandOperand;
306
+ operands[1] = rightHandOperand;
307
+ (, bytes32[] memory results) = _executeOperation(
308
+ Operator.Ne,
309
+ operands,
310
+ TEEType.Bool, // Result type
311
+ 1,
312
+ false
313
+ );
314
+ result = results[0];
315
+ emit Ne(msg.sender, leftHandOperand, rightHandOperand, result);
316
+ }
317
+
318
+ /// @inheritdoc INoxCompute
319
+ function lt(
320
+ bytes32 leftHandOperand,
321
+ bytes32 rightHandOperand
322
+ ) external override returns (bytes32 result) {
323
+ TypeUtils.validateOperationTypes(leftHandOperand, rightHandOperand);
324
+ bytes32[] memory operands = new bytes32[](2);
325
+ operands[0] = leftHandOperand;
326
+ operands[1] = rightHandOperand;
327
+ (, bytes32[] memory results) = _executeOperation(
328
+ Operator.Lt,
329
+ operands,
330
+ TEEType.Bool, // Result type
331
+ 1,
332
+ false
333
+ );
334
+ result = results[0];
335
+ emit Lt(msg.sender, leftHandOperand, rightHandOperand, result);
336
+ }
337
+
338
+ /// @inheritdoc INoxCompute
339
+ function le(
340
+ bytes32 leftHandOperand,
341
+ bytes32 rightHandOperand
342
+ ) external override returns (bytes32 result) {
343
+ TypeUtils.validateOperationTypes(leftHandOperand, rightHandOperand);
344
+ bytes32[] memory operands = new bytes32[](2);
345
+ operands[0] = leftHandOperand;
346
+ operands[1] = rightHandOperand;
347
+ (, bytes32[] memory results) = _executeOperation(
348
+ Operator.Le,
349
+ operands,
350
+ TEEType.Bool, // Result type
351
+ 1,
352
+ false
353
+ );
354
+ result = results[0];
355
+ emit Le(msg.sender, leftHandOperand, rightHandOperand, result);
356
+ }
357
+
358
+ /// @inheritdoc INoxCompute
359
+ function gt(
360
+ bytes32 leftHandOperand,
361
+ bytes32 rightHandOperand
362
+ ) external override returns (bytes32 result) {
363
+ TypeUtils.validateOperationTypes(leftHandOperand, rightHandOperand);
364
+ bytes32[] memory operands = new bytes32[](2);
365
+ operands[0] = leftHandOperand;
366
+ operands[1] = rightHandOperand;
367
+ (, bytes32[] memory results) = _executeOperation(
368
+ Operator.Gt,
369
+ operands,
370
+ TEEType.Bool, // Result type
371
+ 1,
372
+ false
373
+ );
374
+ result = results[0];
375
+ emit Gt(msg.sender, leftHandOperand, rightHandOperand, result);
376
+ }
377
+
378
+ /// @inheritdoc INoxCompute
379
+ function ge(
380
+ bytes32 leftHandOperand,
381
+ bytes32 rightHandOperand
382
+ ) external override returns (bytes32 result) {
383
+ TypeUtils.validateOperationTypes(leftHandOperand, rightHandOperand);
384
+ bytes32[] memory operands = new bytes32[](2);
385
+ operands[0] = leftHandOperand;
386
+ operands[1] = rightHandOperand;
387
+ (, bytes32[] memory results) = _executeOperation(
388
+ Operator.Ge,
389
+ operands,
390
+ TEEType.Bool, // Result type
391
+ 1,
392
+ false
393
+ );
394
+ result = results[0];
395
+ emit Ge(msg.sender, leftHandOperand, rightHandOperand, result);
396
+ }
397
+
398
+ /// @inheritdoc INoxCompute
399
+ function select(
400
+ bytes32 condition,
401
+ bytes32 ifTrue,
402
+ bytes32 ifFalse
403
+ ) external override returns (bytes32 result) {
404
+ TypeUtils.requireType(condition, TEEType.Bool);
405
+ TypeUtils.validateOperationTypes(ifTrue, ifFalse);
406
+ bytes32[] memory operands = new bytes32[](3);
407
+ operands[0] = condition;
408
+ operands[1] = ifTrue;
409
+ operands[2] = ifFalse;
410
+ (, bytes32[] memory results) = _executeOperation(
411
+ Operator.Select,
412
+ operands,
413
+ operands[1].typeOf(), // Result type
414
+ 1,
415
+ false
416
+ );
417
+ result = results[0];
418
+ emit Select(msg.sender, condition, ifTrue, ifFalse, result);
419
+ }
420
+
421
+ /// @inheritdoc INoxCompute
422
+ function transfer(
423
+ bytes32 balanceFrom,
424
+ bytes32 balanceTo,
425
+ bytes32 amount
426
+ ) external override returns (bytes32 success, bytes32 newBalanceFrom, bytes32 newBalanceTo) {
427
+ TypeUtils.validateOperationTypes(balanceFrom, balanceTo, amount);
428
+ bytes32[] memory operands = new bytes32[](3);
429
+ operands[0] = balanceFrom;
430
+ operands[1] = balanceTo;
431
+ operands[2] = amount;
432
+ bytes32[] memory results;
433
+ (success, results) = _executeOperation(
434
+ Operator.Transfer,
435
+ operands,
436
+ operands[0].typeOf(), // Result type
437
+ 2,
438
+ true
439
+ );
440
+ newBalanceFrom = results[0];
441
+ newBalanceTo = results[1];
442
+ emit Transfer(
443
+ msg.sender,
444
+ balanceFrom,
445
+ balanceTo,
446
+ amount,
447
+ success,
448
+ newBalanceFrom,
449
+ newBalanceTo
450
+ );
451
+ }
452
+
453
+ /// @inheritdoc INoxCompute
454
+ function mint(
455
+ bytes32 balanceTo,
456
+ bytes32 amount,
457
+ bytes32 totalSupply
458
+ ) external override returns (bytes32 success, bytes32 newBalanceTo, bytes32 newTotalSupply) {
459
+ TypeUtils.validateOperationTypes(balanceTo, amount, totalSupply);
460
+ bytes32[] memory operands = new bytes32[](3);
461
+ operands[0] = balanceTo;
462
+ operands[1] = amount;
463
+ operands[2] = totalSupply;
464
+ bytes32[] memory results;
465
+ (success, results) = _executeOperation(
466
+ Operator.Mint,
467
+ operands,
468
+ operands[0].typeOf(), // Result type
469
+ 2,
470
+ true
471
+ );
472
+ newBalanceTo = results[0];
473
+ newTotalSupply = results[1];
474
+ emit Mint(
475
+ msg.sender,
476
+ balanceTo,
477
+ amount,
478
+ totalSupply,
479
+ success,
480
+ newBalanceTo,
481
+ newTotalSupply
482
+ );
483
+ }
484
+
485
+ /// @inheritdoc INoxCompute
486
+ function burn(
487
+ bytes32 balanceFrom,
488
+ bytes32 amount,
489
+ bytes32 totalSupply
490
+ ) external override returns (bytes32 success, bytes32 newBalanceFrom, bytes32 newTotalSupply) {
491
+ TypeUtils.validateOperationTypes(balanceFrom, amount, totalSupply);
492
+ bytes32[] memory operands = new bytes32[](3);
493
+ operands[0] = balanceFrom;
494
+ operands[1] = amount;
495
+ operands[2] = totalSupply;
496
+ bytes32[] memory results;
497
+ (success, results) = _executeOperation(
498
+ Operator.Burn,
499
+ operands,
500
+ operands[0].typeOf(), // Result type
501
+ 2,
502
+ true
503
+ );
504
+ newBalanceFrom = results[0];
505
+ newTotalSupply = results[1];
506
+ emit Burn(
507
+ msg.sender,
508
+ balanceFrom,
509
+ amount,
510
+ totalSupply,
511
+ success,
512
+ newBalanceFrom,
513
+ newTotalSupply
514
+ );
515
+ }
516
+
517
+ /**
518
+ * Processes a compute operation on encrypted handles with arithmetic types.
519
+ * - Validates ACL for all input handles
520
+ * - Generates result handles
521
+ * - Grants transient access to msg.sender
522
+ * Note: Caller functions are responsible for type validation.
523
+ * @param operator The operator to apply
524
+ * @param operands Array of operand handles
525
+ * @param resultType TEE type for result handles
526
+ * @param resultCount Number of result handles to generate
527
+ * @param withSuccess Whether to generate a Bool success handle
528
+ * @return success The Bool success handle (bytes32(0) if withSuccess is false)
529
+ * @return results Array of result handles
530
+ */
531
+ // TODO rename to _processOperation.
532
+ function _executeOperation(
533
+ Operator operator,
534
+ bytes32[] memory operands,
535
+ TEEType resultType,
536
+ uint8 resultCount,
537
+ bool withSuccess
538
+ ) private returns (bytes32 success, bytes32[] memory results) {
539
+ _requireDefinedHandles(operands);
540
+ _validateAllowedForAll(msg.sender, operands);
541
+ // The same seed can be used for all result handles because
542
+ // they differ by outputIndex.
543
+ uint256 uniqueSeed = _generateHandleUniqueSeed(operands);
544
+ results = new bytes32[](resultCount);
545
+ for (uint8 i = 0; i < resultCount; i++) {
546
+ results[i] = _generateHandle(
547
+ operator,
548
+ operands,
549
+ resultType,
550
+ i,
551
+ uniqueSeed,
552
+ HandleUtils.ATTR_IS_UNIQUE_HANDLE
553
+ );
554
+ _allowTransient(results[i], msg.sender);
555
+ }
556
+ if (withSuccess) {
557
+ success = _generateHandle(
558
+ operator,
559
+ operands,
560
+ TEEType.Bool,
561
+ resultCount,
562
+ uniqueSeed,
563
+ HandleUtils.ATTR_IS_UNIQUE_HANDLE
564
+ );
565
+ _allowTransient(success, msg.sender);
566
+ }
567
+ }
568
+
569
+ /**
570
+ * Reverts if any operand is bytes32(0) (undefined handle).
571
+ */
572
+ function _requireDefinedHandles(bytes32[] memory operands) private pure {
573
+ for (uint256 i = 0; i < operands.length; i++) {
574
+ require(operands[i] != bytes32(0), UndefinedHandle());
575
+ }
576
+ }
577
+
578
+ /**
579
+ * Generates a complete handle from an operator and its operands.
580
+ *
581
+ * Pre-handle format:
582
+ * keccak256(abi.encode(
583
+ * operator, // Operator identifier (e.g., Add, Sub, WrapAsPublicHandle)
584
+ * operands, // Array of operand handles (or plaintext value)
585
+ * address(this), // NoxCompute contract address
586
+ * uniqueSeed, // Uniqueness seed (0 or counter value)
587
+ * outputIndex // For operations that return multiple outputs
588
+ * ))
589
+ *
590
+ * Handle format (32 bytes):
591
+ * [0] : Handle version
592
+ * [1-4] : Chain ID (4 bytes, uint32)
593
+ * [5] : TEE type
594
+ * [6] : Attributes (bit 0 = isUniqueHandle)
595
+ * [7-31] : Truncated pre-handle hash (25 bytes)
596
+ *
597
+ * @param operator The operator to apply
598
+ * @param operands Array of operand handles
599
+ * @param handleType The TEE type to encode in the handle
600
+ * @param outputIndex Index for operations returning multiple outputs
601
+ * @param uniqueSeed Uniqueness seed (0 for wrapAsPublicHandle and unique operands)
602
+ * @param attrs Attributes byte (0x00 for public handle, 0x01 for confidential)
603
+ * @return result The complete handle with metadata appended
604
+ */
605
+ function _generateHandle(
606
+ Operator operator,
607
+ bytes32[] memory operands,
608
+ TEEType handleType,
609
+ uint8 outputIndex,
610
+ uint256 uniqueSeed,
611
+ bytes1 attrs
612
+ ) private view returns (bytes32 result) {
613
+ result = keccak256(abi.encode(operator, operands, address(this), uniqueSeed, outputIndex));
614
+ // Shift hash to bytes 7-31 (truncate to 25 bytes), leaving bytes 0-6 free for metadata.
615
+ result = result >> (7 * 8);
616
+ result = result | bytes32(bytes1(uint8(HANDLE_VERSION)));
617
+ result = result | (bytes32(bytes4(uint32(block.chainid))) >> (1 * 8));
618
+ result = result | (bytes32(bytes1(uint8(handleType))) >> (5 * 8));
619
+ result = result | (bytes32(attrs) >> (6 * 8));
620
+ }
621
+
622
+ /**
623
+ * Determines the uniqueness seed for a confidential operation.
624
+ * If at least one operand has isUniqueHandle=1, returns 0 (no storage access needed).
625
+ * If all operands are public handles, increments a storage counter to guarantee uniqueness.
626
+ * @param operands Array of operand handles
627
+ * @return The uniqueness seed
628
+ */
629
+ function _generateHandleUniqueSeed(bytes32[] memory operands) private returns (uint256) {
630
+ for (uint256 i = 0; i < operands.length; i++) {
631
+ if (!HandleUtils.isPublicHandle(operands[i])) {
632
+ return 0;
633
+ }
634
+ }
635
+ // All operands are public handles: need storage counter for uniqueness
636
+ NoxComputeStorage storage $ = _getNoxComputeStorage();
637
+ return ++$.uniqueSeedCounter;
638
+ }
639
+
640
+ /**
641
+ * Emits events to seed the zero handles for all supported types. This allows off-chain
642
+ * services to recognize the zero handle for each type without needing to hardcode them.
643
+ */
644
+ function _emitZeroHandleSeeds() internal {
645
+ TEEType[] memory types = TypeUtils.allCurrentlySupportedTypes();
646
+ for (uint i = 0; i < types.length; i++) {
647
+ emit WrapAsPublicHandle(
648
+ address(this),
649
+ bytes32(0),
650
+ types[i],
651
+ HandleUtils.zeroHandle(types[i])
652
+ );
653
+ }
654
+ }
655
+ }