@inco/lightning 0.7.10 → 0.8.0-devnet-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/manifest.yaml +22 -0
- package/package.json +1 -1
- package/src/IncoLightning.sol +4 -0
- package/src/Lib.alphanet.sol +62 -6
- package/src/Lib.demonet.sol +62 -6
- package/src/Lib.devnet.sol +63 -7
- package/src/Lib.sol +63 -7
- package/src/Lib.template.sol +65 -9
- package/src/Lib.testnet.sol +62 -6
- package/src/interfaces/IIncoLightning.sol +2 -0
- package/src/libs/incoLightning_alphanet_v0_297966649.sol +62 -6
- package/src/libs/incoLightning_alphanet_v1_725458969.sol +62 -6
- package/src/libs/incoLightning_alphanet_v2_976644394.sol +62 -6
- package/src/libs/incoLightning_demonet_v0_863421733.sol +62 -6
- package/src/libs/incoLightning_demonet_v2_467437523.sol +62 -6
- package/src/libs/incoLightning_devnet_v0_340846814.sol +62 -6
- package/src/libs/incoLightning_devnet_v1_904635675.sol +62 -6
- package/src/libs/incoLightning_devnet_v2_295237520.sol +62 -6
- package/src/libs/incoLightning_devnet_v3_976859633.sol +534 -0
- package/src/libs/incoLightning_testnet_v0_183408998.sol +62 -6
- package/src/libs/incoLightning_testnet_v2_889158349.sol +62 -6
- package/src/lightning-parts/Fee.sol +10 -14
- package/src/lightning-parts/TEELifecycle.sol +11 -12
- package/src/lightning-parts/TEELifecycle.types.sol +1 -4
- package/src/periphery/IncoUtils.sol +42 -0
- package/src/test/AddTwo.sol +3 -3
- package/src/test/FakeIncoInfra/MockRemoteAttestation.sol +2 -3
- package/src/test/IncoTest.sol +2 -2
- package/src/test/TEELifecycle/TEELifecycleMockTest.t.sol +1 -1
- package/src/test/TestFakeInfra.t.sol +263 -0
- package/src/test/TestFeeWithdrawal.t.sol +60 -0
- package/src/test/TestIncoUtils.t.sol +242 -0
- package/src/version/IncoLightningConfig.sol +2 -2
- package/src/IIncoLightning.sol +0 -22
|
@@ -16,6 +16,11 @@ function typeOf(bytes32 handle) pure returns (ETypes) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
library e {
|
|
19
|
+
error CallFailedAfterFeeRefresh();
|
|
20
|
+
|
|
21
|
+
/// @dev slot to store the fee for inco operations
|
|
22
|
+
bytes32 private constant FEE_SLOT = keccak256("inco.fee");
|
|
23
|
+
|
|
19
24
|
function sanitize(euint256 a) internal returns (euint256) {
|
|
20
25
|
if (euint256.unwrap(a) == bytes32(0)) {
|
|
21
26
|
return asEuint256(0);
|
|
@@ -355,17 +360,22 @@ library e {
|
|
|
355
360
|
|
|
356
361
|
/// @dev costs the inco fee
|
|
357
362
|
function rand() internal returns (euint256) {
|
|
358
|
-
|
|
363
|
+
bytes32 result = _callWithFeeRetry(abi.encodeWithSelector(inco.eRand.selector, ETypes.Uint256));
|
|
364
|
+
return euint256.wrap(result);
|
|
359
365
|
}
|
|
360
366
|
|
|
361
367
|
/// @dev costs the inco fee
|
|
362
368
|
function randBounded(uint256 upperBound) internal returns (euint256) {
|
|
363
|
-
|
|
369
|
+
bytes32 boundHandle = euint256.unwrap(asEuint256(upperBound));
|
|
370
|
+
bytes32 result = _callWithFeeRetry(abi.encodeWithSelector(inco.eRandBounded.selector, boundHandle, ETypes.Uint256));
|
|
371
|
+
return euint256.wrap(result);
|
|
364
372
|
}
|
|
365
373
|
|
|
366
374
|
/// @dev costs the inco fee
|
|
367
375
|
function randBounded(euint256 upperBound) internal returns (euint256) {
|
|
368
|
-
|
|
376
|
+
bytes32 boundHandle = euint256.unwrap(s(upperBound));
|
|
377
|
+
bytes32 result = _callWithFeeRetry(abi.encodeWithSelector(inco.eRandBounded.selector, boundHandle, ETypes.Uint256));
|
|
378
|
+
return euint256.wrap(result);
|
|
369
379
|
}
|
|
370
380
|
|
|
371
381
|
function asEuint256(uint256 a) internal returns (euint256) {
|
|
@@ -397,7 +407,8 @@ library e {
|
|
|
397
407
|
/// @notice Creates a new encrypted uint256 for the given user.
|
|
398
408
|
/// @dev costs the inco fee
|
|
399
409
|
function newEuint256(bytes memory ciphertext, address user) internal returns (euint256) {
|
|
400
|
-
|
|
410
|
+
bytes32 result = _callWithFeeRetry(abi.encodeWithSelector(inco.newEuint256.selector, ciphertext, user));
|
|
411
|
+
return euint256.wrap(result);
|
|
401
412
|
}
|
|
402
413
|
|
|
403
414
|
/// @notice Creates a new encrypted bool assuming msg.sender is the user
|
|
@@ -409,7 +420,8 @@ library e {
|
|
|
409
420
|
/// @notice Creates a new encrypted bool for the given user.
|
|
410
421
|
/// @dev costs the inco fee
|
|
411
422
|
function newEbool(bytes memory ciphertext, address user) internal returns (ebool) {
|
|
412
|
-
|
|
423
|
+
bytes32 result = _callWithFeeRetry(abi.encodeWithSelector(inco.newEbool.selector, ciphertext, user));
|
|
424
|
+
return ebool.wrap(result);
|
|
413
425
|
}
|
|
414
426
|
|
|
415
427
|
/// @notice Creates a new encrypted address assuming msg.sender is the user
|
|
@@ -421,7 +433,8 @@ library e {
|
|
|
421
433
|
/// @notice Creates a new encrypted address for the given user.
|
|
422
434
|
/// @dev costs the inco fee
|
|
423
435
|
function newEaddress(bytes memory ciphertext, address user) internal returns (eaddress) {
|
|
424
|
-
|
|
436
|
+
bytes32 result = _callWithFeeRetry(abi.encodeWithSelector(inco.newEaddress.selector, ciphertext, user));
|
|
437
|
+
return eaddress.wrap(result);
|
|
425
438
|
}
|
|
426
439
|
|
|
427
440
|
function allow(euint256 a, address to) internal {
|
|
@@ -475,4 +488,47 @@ library e {
|
|
|
475
488
|
function select(ebool control, eaddress ifTrue, eaddress ifFalse) internal returns (eaddress) {
|
|
476
489
|
return eaddress.wrap(inco.eIfThenElse(s(control), eaddress.unwrap(s(ifTrue)), eaddress.unwrap(s(ifFalse))));
|
|
477
490
|
}
|
|
491
|
+
|
|
492
|
+
/// @dev Store fee in the custom slot
|
|
493
|
+
/// @param _fee The fee to store
|
|
494
|
+
function _setFee(uint256 _fee) private {
|
|
495
|
+
bytes32 slot = FEE_SLOT;
|
|
496
|
+
assembly {
|
|
497
|
+
sstore(slot, _fee)
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/// @dev Retrieve fee from the custom slot
|
|
502
|
+
/// @return fee The stored fee
|
|
503
|
+
function _getFee() private view returns (uint256 fee) {
|
|
504
|
+
bytes32 slot = FEE_SLOT;
|
|
505
|
+
assembly {
|
|
506
|
+
fee := sload(slot)
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/// @dev Get current fee with fallback to inco.getFee() if not cached
|
|
511
|
+
function getCurrentFee() private returns (uint256) {
|
|
512
|
+
uint256 cachedFee = _getFee();
|
|
513
|
+
if (cachedFee == 0) {
|
|
514
|
+
cachedFee = inco.getFee();
|
|
515
|
+
_setFee(cachedFee);
|
|
516
|
+
}
|
|
517
|
+
return cachedFee;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/// @dev Execute a call to inco with fee, retrying with fresh fee if it fails
|
|
521
|
+
/// @param callData The encoded function call (use abi.encodeWithSelector)
|
|
522
|
+
/// @return result The bytes32 result from the call
|
|
523
|
+
function _callWithFeeRetry(bytes memory callData) private returns (bytes32) {
|
|
524
|
+
uint256 fee = getCurrentFee();
|
|
525
|
+
(bool success, bytes memory result) = address(inco).call{value: fee}(callData);
|
|
526
|
+
if (!success) {
|
|
527
|
+
fee = inco.getFee();
|
|
528
|
+
_setFee(fee);
|
|
529
|
+
(success, result) = address(inco).call{value: fee}(callData);
|
|
530
|
+
require(success, CallFailedAfterFeeRefresh());
|
|
531
|
+
}
|
|
532
|
+
return abi.decode(result, (bytes32));
|
|
533
|
+
}
|
|
478
534
|
}
|
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
pragma solidity ^0.8;
|
|
3
3
|
|
|
4
4
|
// the fee should be modified through upgrades to limit gas cost
|
|
5
|
-
uint256 constant FEE = 0.
|
|
5
|
+
uint256 constant FEE = 0.000001 ether;
|
|
6
6
|
|
|
7
7
|
/// @notice Fee utils for lightning functions that require a fee
|
|
8
8
|
/// @dev the fee may be changed through upgrades, develop your apps accordingly!
|
|
9
9
|
abstract contract Fee {
|
|
10
10
|
|
|
11
11
|
error FeeNotPaid();
|
|
12
|
+
error FeeWithdrawalFailed();
|
|
13
|
+
error NoFeesToWithdraw();
|
|
12
14
|
|
|
13
15
|
/// @notice the fee to pay through msg.value for inputs and randomness IT MAY CHANGE
|
|
14
16
|
function getFee() public pure returns (uint256) {
|
|
@@ -25,19 +27,13 @@ abstract contract Fee {
|
|
|
25
27
|
_;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
uint256 spent = balanceBefore > balanceAfter ? balanceBefore - balanceAfter : 0;
|
|
36
|
-
uint256 refund = msg.value > spent ? msg.value - spent : 0;
|
|
37
|
-
if (refund > 0) {
|
|
38
|
-
(bool success,) = msg.sender.call{value: refund}("");
|
|
39
|
-
require(success, "Refund failed");
|
|
40
|
-
}
|
|
30
|
+
/// @notice Internal helper to withdraw accumulated fees to a recipient
|
|
31
|
+
/// @param recipient The address to send the fees to
|
|
32
|
+
function _withdrawFeesTo(address recipient) internal {
|
|
33
|
+
uint256 fees = address(this).balance;
|
|
34
|
+
require(fees > 0, NoFeesToWithdraw());
|
|
35
|
+
(bool success,) = payable(recipient).call{value: fees}("");
|
|
36
|
+
require(success, FeeWithdrawalFailed());
|
|
41
37
|
}
|
|
42
38
|
|
|
43
39
|
}
|
|
@@ -29,8 +29,7 @@ contract TEELifecycleStorage {
|
|
|
29
29
|
// @dev The index of the TEE version is the version number
|
|
30
30
|
bytes32[] approvedTeeVersions;
|
|
31
31
|
// @notice The Network key
|
|
32
|
-
|
|
33
|
-
bytes eciesPubkey;
|
|
32
|
+
bytes networkPubkey;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
bytes32 private constant TEE_LIFECYCLE_STORAGE_LOCATION = keccak256("inco.storage.TEELifecycle");
|
|
@@ -80,9 +79,9 @@ abstract contract TEELifecycle is
|
|
|
80
79
|
event SignerHasUpdatedTDX(address indexed eoaSigner, bytes32 indexed mrAggregated);
|
|
81
80
|
|
|
82
81
|
// Constants
|
|
83
|
-
bytes32 public constant BOOTSTRAP_RESULT_STRUCT_HASH = keccak256(bytes("BootstrapResult(bytes
|
|
84
|
-
bytes32 public constant UPGRADE_RESULT_STRUCT_HASH = keccak256(bytes("UpgradeResult(bytes
|
|
85
|
-
bytes32 public constant ADD_NODE_RESULT_STRUCT_HASH = keccak256(bytes("AddNodeResult(bytes
|
|
82
|
+
bytes32 public constant BOOTSTRAP_RESULT_STRUCT_HASH = keccak256(bytes("BootstrapResult(bytes networkPubkey)"));
|
|
83
|
+
bytes32 public constant UPGRADE_RESULT_STRUCT_HASH = keccak256(bytes("UpgradeResult(bytes networkPubkey)"));
|
|
84
|
+
bytes32 public constant ADD_NODE_RESULT_STRUCT_HASH = keccak256(bytes("AddNodeResult(bytes networkPubkey)"));
|
|
86
85
|
|
|
87
86
|
uint16 public constant QUOTE_VERIFIER_VERSION = 4;
|
|
88
87
|
|
|
@@ -141,7 +140,7 @@ abstract contract TEELifecycle is
|
|
|
141
140
|
|
|
142
141
|
// Update contract publicly viewable state
|
|
143
142
|
addSigner(reportDataSigner);
|
|
144
|
-
$.
|
|
143
|
+
$.networkPubkey = bootstrapResult.networkPubkey;
|
|
145
144
|
|
|
146
145
|
emit BootstrapStageComplete(reportDataSigner, bootstrapResult);
|
|
147
146
|
}
|
|
@@ -163,7 +162,7 @@ abstract contract TEELifecycle is
|
|
|
163
162
|
|
|
164
163
|
require(isBootstrapComplete(), BootstrapNotComplete());
|
|
165
164
|
// Make sure the quote's network pubkey is the same as the one in the bootstrap result
|
|
166
|
-
require(keccak256($.
|
|
165
|
+
require(keccak256($.networkPubkey) == keccak256(upgradeResult.networkPubkey), InvalidNetworkPubkey());
|
|
167
166
|
// Make sure the new MR_AGGREGATED has been pre-approved.
|
|
168
167
|
bool isApproved = _isApprovedTeeVersion(newMrAggregated);
|
|
169
168
|
require(isApproved, TEEVersionNotFound());
|
|
@@ -188,7 +187,7 @@ abstract contract TEELifecycle is
|
|
|
188
187
|
|
|
189
188
|
require(isBootstrapComplete(), BootstrapNotComplete());
|
|
190
189
|
// Make sure the quote's network pubkey is the same as the one in the bootstrap result
|
|
191
|
-
require(keccak256($.
|
|
190
|
+
require(keccak256($.networkPubkey) == keccak256(addNodeResult.networkPubkey), InvalidNetworkPubkey());
|
|
192
191
|
// Make sure the new MR_AGGREGATED has been pre-approved.
|
|
193
192
|
bool isApproved = _isApprovedTeeVersion(newMrAggregated);
|
|
194
193
|
require(isApproved, TEEVersionNotFound());
|
|
@@ -257,7 +256,7 @@ abstract contract TEELifecycle is
|
|
|
257
256
|
function isBootstrapComplete() public view returns (bool) {
|
|
258
257
|
// The network pubkey is set once and once only, during the bootstrap process
|
|
259
258
|
// So we can use the length of the network pubkey to check if the bootstrap is complete
|
|
260
|
-
return getTeeLifecycleStorage().
|
|
259
|
+
return getTeeLifecycleStorage().networkPubkey.length > 0;
|
|
261
260
|
}
|
|
262
261
|
|
|
263
262
|
/**
|
|
@@ -384,14 +383,14 @@ abstract contract TEELifecycle is
|
|
|
384
383
|
return $.approvedTeeVersions[index];
|
|
385
384
|
}
|
|
386
385
|
|
|
387
|
-
function
|
|
388
|
-
return getTeeLifecycleStorage().
|
|
386
|
+
function networkPubkey() external view returns (bytes memory) {
|
|
387
|
+
return getTeeLifecycleStorage().networkPubkey;
|
|
389
388
|
}
|
|
390
389
|
|
|
391
390
|
function bootstrapResultDigest(BootstrapResult memory bootstrapResult) public view returns (bytes32) {
|
|
392
391
|
return
|
|
393
392
|
_hashTypedDataV4(
|
|
394
|
-
keccak256(abi.encode(BOOTSTRAP_RESULT_STRUCT_HASH, keccak256(bootstrapResult.
|
|
393
|
+
keccak256(abi.encode(BOOTSTRAP_RESULT_STRUCT_HASH, keccak256(bootstrapResult.networkPubkey)))
|
|
395
394
|
);
|
|
396
395
|
}
|
|
397
396
|
|
|
@@ -5,10 +5,7 @@ pragma solidity ^0.8.19;
|
|
|
5
5
|
* @notice A struct representing what the TDX EOA signs during the bootstrap process.
|
|
6
6
|
*/
|
|
7
7
|
struct BootstrapResult {
|
|
8
|
-
|
|
9
|
-
// https://github.com/Inco-fhevm/inco-monorepo/issues/983
|
|
10
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
11
|
-
bytes ecies_pubkey;
|
|
8
|
+
bytes networkPubkey;
|
|
12
9
|
}
|
|
13
10
|
|
|
14
11
|
/**
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// SPDX-License-Identifier: No License
|
|
2
|
+
pragma solidity ^0.8;
|
|
3
|
+
|
|
4
|
+
import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol";
|
|
5
|
+
|
|
6
|
+
// Re-export FEE constant for convenience - consumers can import both IncoUtils and FEE from this file
|
|
7
|
+
import {FEE} from "../lightning-parts/Fee.sol";
|
|
8
|
+
|
|
9
|
+
contract IncoUtils {
|
|
10
|
+
|
|
11
|
+
error RefundFailed();
|
|
12
|
+
error ReentrantCall();
|
|
13
|
+
|
|
14
|
+
uint256 private constant NOT_ENTERED = 1;
|
|
15
|
+
uint256 private constant ENTERED = 2;
|
|
16
|
+
|
|
17
|
+
bytes32 private constant REENTRANCY_GUARD_SLOT = keccak256("inco.storage.IncoUtils.reentrancyGuard");
|
|
18
|
+
|
|
19
|
+
/// @notice Refund the difference between msg.value and what was actually spent
|
|
20
|
+
/// @dev Assumes all outflows and inflows to the contract are due to the user; refund is capped at msg.value
|
|
21
|
+
/// @dev Includes built-in reentrancy protection using OZ StorageSlot pattern (modifiers cannot use other modifiers)
|
|
22
|
+
modifier refundUnspent() {
|
|
23
|
+
StorageSlot.Uint256Slot storage status = StorageSlot.getUint256Slot(REENTRANCY_GUARD_SLOT);
|
|
24
|
+
|
|
25
|
+
require(status.value != ENTERED, ReentrantCall());
|
|
26
|
+
status.value = ENTERED;
|
|
27
|
+
|
|
28
|
+
uint256 balanceBefore = address(this).balance;
|
|
29
|
+
_;
|
|
30
|
+
uint256 balanceAfter = address(this).balance;
|
|
31
|
+
uint256 spent = balanceBefore > balanceAfter ? balanceBefore - balanceAfter : 0;
|
|
32
|
+
uint256 refund = msg.value > spent ? msg.value - spent : 0;
|
|
33
|
+
|
|
34
|
+
if (refund > 0) {
|
|
35
|
+
(bool success,) = msg.sender.call{value: refund}("");
|
|
36
|
+
require(success, RefundFailed());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
status.value = NOT_ENTERED;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
}
|
package/src/test/AddTwo.sol
CHANGED
|
@@ -3,12 +3,12 @@ pragma solidity ^0.8;
|
|
|
3
3
|
|
|
4
4
|
import {euint256, ebool} from "../Types.sol";
|
|
5
5
|
import {IncoLightning} from "../IncoLightning.sol";
|
|
6
|
-
import {
|
|
6
|
+
import {IncoUtils, FEE} from "../periphery/IncoUtils.sol";
|
|
7
7
|
import {DecryptionAttestation} from "../lightning-parts/DecryptionAttester.types.sol";
|
|
8
8
|
|
|
9
9
|
// To implement such a contract, we would normally import e form Lib.sol. For test purposes, we take inco as
|
|
10
10
|
// a constructor argument instead, so we can test it from other deployment addresses.
|
|
11
|
-
contract AddTwo is
|
|
11
|
+
contract AddTwo is IncoUtils {
|
|
12
12
|
|
|
13
13
|
IncoLightning inco;
|
|
14
14
|
|
|
@@ -36,7 +36,7 @@ contract AddTwo is Fee {
|
|
|
36
36
|
refundUnspent
|
|
37
37
|
returns (euint256 result, euint256 resultRevealed)
|
|
38
38
|
{
|
|
39
|
-
euint256 value = inco.newEuint256{value:
|
|
39
|
+
euint256 value = inco.newEuint256{value: FEE}(uint256EInput, msg.sender);
|
|
40
40
|
result = addTwo(value);
|
|
41
41
|
|
|
42
42
|
inco.allow(euint256.unwrap(result), address(this));
|
|
@@ -43,7 +43,7 @@ contract MockRemoteAttestation is TestUtils {
|
|
|
43
43
|
// Helper function to create a successful bootstrap result
|
|
44
44
|
function successfulBootstrapResult(
|
|
45
45
|
ITEELifecycle teeLifecycle,
|
|
46
|
-
bytes memory
|
|
46
|
+
bytes memory networkPubkey,
|
|
47
47
|
address signer,
|
|
48
48
|
uint256 signerPrivKey
|
|
49
49
|
)
|
|
@@ -55,14 +55,13 @@ contract MockRemoteAttestation is TestUtils {
|
|
|
55
55
|
bytes32 mrAggregated
|
|
56
56
|
)
|
|
57
57
|
{
|
|
58
|
-
bytes memory eciesPubkey = eciesPublicKey;
|
|
59
58
|
// See DEFAULT_MRTD in attestation/src/remote_attestation.rs
|
|
60
59
|
bytes memory mrtd =
|
|
61
60
|
hex"010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101";
|
|
62
61
|
// See DEFAULT_MR_AGGREGATED in attestation/src/remote_attestation.rs to
|
|
63
62
|
// see the calculation of the default value.
|
|
64
63
|
mrAggregated = hex"c3a67bac251d4946d7b17481d39631676042fe3afab06e70c22105ad8383c19f";
|
|
65
|
-
bootstrapResult = BootstrapResult({
|
|
64
|
+
bootstrapResult = BootstrapResult({networkPubkey: networkPubkey});
|
|
66
65
|
|
|
67
66
|
quote = createQuote(mrtd, signer);
|
|
68
67
|
signature = signBootstrapResult(bootstrapResult, signerPrivKey, teeLifecycle);
|
package/src/test/IncoTest.sol
CHANGED
|
@@ -39,12 +39,12 @@ contract IncoTest is MockOpHandler, DeployUtils, FakeDecryptionAttester, MockRem
|
|
|
39
39
|
function setUp() public virtual {
|
|
40
40
|
deployCreateX();
|
|
41
41
|
vm.startPrank(testDeployer);
|
|
42
|
-
vm.setEnv("
|
|
42
|
+
vm.setEnv("SHOULD_SETUP_TEE_SIGNER", "true"); // results in a network pubkey and decrypt signer being populated in the TEE Lifecycle
|
|
43
43
|
(IIncoLightning proxy,) = deployIncoLightningUsingConfig({
|
|
44
44
|
deployer: testDeployer,
|
|
45
45
|
// The highest precedent deployment
|
|
46
46
|
// We select the pepper that will be used that will be generated in the lib.sol (usually "testnet"), but currently "alphanet" has higher major version
|
|
47
|
-
pepper: "
|
|
47
|
+
pepper: "devnet",
|
|
48
48
|
quoteVerifier: new FakeQuoteVerifier()
|
|
49
49
|
});
|
|
50
50
|
IOwnable(address(proxy)).transferOwnership(owner);
|
|
@@ -138,7 +138,7 @@ contract TEELifecycleMockTest is MockRemoteAttestation, TEELifecycle {
|
|
|
138
138
|
{
|
|
139
139
|
(bootstrapPartyPrivkey, bootstrapPartyAddress) = getLabeledKeyPair("bootstrapParty");
|
|
140
140
|
mrAggregated = testMrAggregated;
|
|
141
|
-
bootstrapResult = BootstrapResult({
|
|
141
|
+
bootstrapResult = BootstrapResult({networkPubkey: testNetworkPubkey});
|
|
142
142
|
|
|
143
143
|
quote = createQuote(testMrtd, bootstrapPartyAddress);
|
|
144
144
|
signature = signBootstrapResult(bootstrapResult, bootstrapPartyPrivkey);
|
|
@@ -14,6 +14,7 @@ contract TakesEInput is IncoTest {
|
|
|
14
14
|
|
|
15
15
|
euint256 public a;
|
|
16
16
|
ebool public b;
|
|
17
|
+
eaddress public c;
|
|
17
18
|
uint256 public decryptedA;
|
|
18
19
|
|
|
19
20
|
function setA(bytes memory uint256EInput) external {
|
|
@@ -25,6 +26,10 @@ contract TakesEInput is IncoTest {
|
|
|
25
26
|
b = boolEInput.newEbool(msg.sender);
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
function setC(bytes memory addressEInput) external {
|
|
30
|
+
c = addressEInput.newEaddress(msg.sender);
|
|
31
|
+
}
|
|
32
|
+
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
// its meta: this is testing correct behavior of our testing infrastructure
|
|
@@ -238,6 +243,264 @@ contract TestFakeInfra is IncoTest {
|
|
|
238
243
|
assertEq(getUint256Value(a), 1);
|
|
239
244
|
}
|
|
240
245
|
|
|
246
|
+
function testERandBoundedWithEuint256() public {
|
|
247
|
+
euint256 bound = e.asEuint256(100);
|
|
248
|
+
processAllOperations();
|
|
249
|
+
euint256 a = e.randBounded(bound);
|
|
250
|
+
processAllOperations();
|
|
251
|
+
assertEq(getUint256Value(a), 1);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ============ FEE CACHING TESTS ============
|
|
255
|
+
|
|
256
|
+
// The fee slot used by the library
|
|
257
|
+
bytes32 constant FEE_SLOT = keccak256("inco.fee");
|
|
258
|
+
|
|
259
|
+
function testFeeCachingOnRand() public {
|
|
260
|
+
// Verify the slot is empty before any operation
|
|
261
|
+
bytes32 cachedFeeBefore = vm.load(address(this), FEE_SLOT);
|
|
262
|
+
assertEq(uint256(cachedFeeBefore), 0, "Fee should not be cached initially");
|
|
263
|
+
|
|
264
|
+
// Call rand which should cache the fee
|
|
265
|
+
euint256 a = e.rand();
|
|
266
|
+
processAllOperations();
|
|
267
|
+
assertEq(getUint256Value(a), 1);
|
|
268
|
+
|
|
269
|
+
// Verify the fee is now cached
|
|
270
|
+
bytes32 cachedFeeAfter = vm.load(address(this), FEE_SLOT);
|
|
271
|
+
assertEq(uint256(cachedFeeAfter), inco.getFee(), "Cached fee should match inco.getFee()");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function testFeeCachingPersistsAcrossOperations() public {
|
|
275
|
+
// Verify the slot is empty before any operation
|
|
276
|
+
bytes32 cachedFeeBefore = vm.load(address(this), FEE_SLOT);
|
|
277
|
+
assertEq(uint256(cachedFeeBefore), 0, "Fee should not be cached initially");
|
|
278
|
+
|
|
279
|
+
// First operation caches the fee
|
|
280
|
+
e.rand();
|
|
281
|
+
processAllOperations();
|
|
282
|
+
|
|
283
|
+
// Get the cached fee
|
|
284
|
+
bytes32 cachedFeeFirst = vm.load(address(this), FEE_SLOT);
|
|
285
|
+
assertEq(uint256(cachedFeeFirst), inco.getFee(), "Fee should be cached after first operation");
|
|
286
|
+
|
|
287
|
+
// Second operation should use cached fee
|
|
288
|
+
e.rand();
|
|
289
|
+
processAllOperations();
|
|
290
|
+
|
|
291
|
+
// Verify fee is still the same (wasn't re-fetched)
|
|
292
|
+
bytes32 cachedFeeSecond = vm.load(address(this), FEE_SLOT);
|
|
293
|
+
assertEq(uint256(cachedFeeFirst), uint256(cachedFeeSecond), "Cached fee should persist across operations");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function testFeeCachingOnRandBounded() public {
|
|
297
|
+
// Verify the slot is empty before any operation
|
|
298
|
+
bytes32 cachedFeeBefore = vm.load(address(this), FEE_SLOT);
|
|
299
|
+
assertEq(uint256(cachedFeeBefore), 0, "Fee should not be cached initially");
|
|
300
|
+
|
|
301
|
+
// Call randBounded which should cache the fee
|
|
302
|
+
euint256 a = e.randBounded(100);
|
|
303
|
+
processAllOperations();
|
|
304
|
+
assertEq(getUint256Value(a), 1);
|
|
305
|
+
|
|
306
|
+
// Verify the fee is now cached
|
|
307
|
+
bytes32 cachedFeeAfter = vm.load(address(this), FEE_SLOT);
|
|
308
|
+
assertEq(uint256(cachedFeeAfter), inco.getFee(), "Cached fee should match inco.getFee()");
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function testFeeCachingOnNewEuint256() public {
|
|
312
|
+
// Create a contract that will use e.newEuint256
|
|
313
|
+
TakesEInput inputContract = new TakesEInput();
|
|
314
|
+
vm.deal(address(inputContract), 1 ether);
|
|
315
|
+
|
|
316
|
+
// Verify the slot is empty in the input contract before operation
|
|
317
|
+
bytes32 cachedFeeBefore = vm.load(address(inputContract), FEE_SLOT);
|
|
318
|
+
assertEq(uint256(cachedFeeBefore), 0, "Fee should not be cached initially");
|
|
319
|
+
|
|
320
|
+
// Call setA which uses e.newEuint256 internally
|
|
321
|
+
address self = address(this);
|
|
322
|
+
bytes memory ciphertext = fakePrepareEuint256Ciphertext(42, self, address(inputContract));
|
|
323
|
+
inputContract.setA(ciphertext);
|
|
324
|
+
processAllOperations();
|
|
325
|
+
|
|
326
|
+
// Verify the fee is now cached in the input contract
|
|
327
|
+
bytes32 cachedFeeAfter = vm.load(address(inputContract), FEE_SLOT);
|
|
328
|
+
assertEq(uint256(cachedFeeAfter), inco.getFee(), "Cached fee should match inco.getFee()");
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function testFeeCachingOnNewEbool() public {
|
|
332
|
+
// Create a contract that will use e.newEbool
|
|
333
|
+
TakesEInput inputContract = new TakesEInput();
|
|
334
|
+
vm.deal(address(inputContract), 1 ether);
|
|
335
|
+
|
|
336
|
+
// Verify the slot is empty in the input contract before operation
|
|
337
|
+
bytes32 cachedFeeBefore = vm.load(address(inputContract), FEE_SLOT);
|
|
338
|
+
assertEq(uint256(cachedFeeBefore), 0, "Fee should not be cached initially");
|
|
339
|
+
|
|
340
|
+
// Call setB which uses e.newEbool internally
|
|
341
|
+
address self = address(this);
|
|
342
|
+
bytes memory ciphertext = fakePrepareEboolCiphertext(true, self, address(inputContract));
|
|
343
|
+
inputContract.setB(ciphertext);
|
|
344
|
+
processAllOperations();
|
|
345
|
+
|
|
346
|
+
// Verify the fee is now cached in the input contract
|
|
347
|
+
bytes32 cachedFeeAfter = vm.load(address(inputContract), FEE_SLOT);
|
|
348
|
+
assertEq(uint256(cachedFeeAfter), inco.getFee(), "Cached fee should match inco.getFee()");
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function testFeeCachingOnEAddress() public {
|
|
352
|
+
// Create a contract that will use e.newEaddress
|
|
353
|
+
TakesEInput inputContract = new TakesEInput();
|
|
354
|
+
vm.deal(address(inputContract), 1 ether);
|
|
355
|
+
|
|
356
|
+
// Verify the slot is empty in the input contract before operation
|
|
357
|
+
bytes32 cachedFeeBefore = vm.load(address(inputContract), FEE_SLOT);
|
|
358
|
+
assertEq(uint256(cachedFeeBefore), 0, "Fee should not be cached initially");
|
|
359
|
+
|
|
360
|
+
// Call setC which uses e.newEaddress internally
|
|
361
|
+
address self = address(this);
|
|
362
|
+
bytes memory ciphertext = fakePrepareEaddressCiphertext(alice, self, address(inputContract));
|
|
363
|
+
inputContract.setC(ciphertext);
|
|
364
|
+
processAllOperations();
|
|
365
|
+
|
|
366
|
+
// Verify the fee is now cached in the input contract
|
|
367
|
+
bytes32 cachedFeeAfter = vm.load(address(inputContract), FEE_SLOT);
|
|
368
|
+
assertEq(uint256(cachedFeeAfter), inco.getFee(), "Cached fee should match inco.getFee()");
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function testFeeRetryWithStaleCachedFee() public {
|
|
372
|
+
// First, cache a valid fee by calling rand
|
|
373
|
+
e.rand();
|
|
374
|
+
processAllOperations();
|
|
375
|
+
|
|
376
|
+
// Now manually set the cached fee to a wrong value
|
|
377
|
+
// This simulates a fee increase after caching
|
|
378
|
+
vm.store(address(this), FEE_SLOT, bytes32(uint256(1)));
|
|
379
|
+
|
|
380
|
+
// Verify the stale fee is set
|
|
381
|
+
bytes32 staleFee = vm.load(address(this), FEE_SLOT);
|
|
382
|
+
assertEq(uint256(staleFee), 1, "Stale fee should be 1 wei");
|
|
383
|
+
|
|
384
|
+
// Call rand again - the first call with stale fee should fail,
|
|
385
|
+
// but retry with fresh fee should succeed (no revert = success)
|
|
386
|
+
euint256 a = e.rand();
|
|
387
|
+
processAllOperations();
|
|
388
|
+
|
|
389
|
+
// Verify we got a valid handle (non-zero)
|
|
390
|
+
assertTrue(euint256.unwrap(a) != bytes32(0), "Should return a valid handle");
|
|
391
|
+
|
|
392
|
+
// Verify the fee cache was updated to the correct value
|
|
393
|
+
bytes32 updatedFee = vm.load(address(this), FEE_SLOT);
|
|
394
|
+
assertEq(uint256(updatedFee), inco.getFee(), "Fee should be updated after retry");
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function testFeeRetryOnRandBounded() public {
|
|
398
|
+
// Cache a valid fee first
|
|
399
|
+
e.rand();
|
|
400
|
+
processAllOperations();
|
|
401
|
+
|
|
402
|
+
// Set stale fee
|
|
403
|
+
vm.store(address(this), FEE_SLOT, bytes32(uint256(1)));
|
|
404
|
+
|
|
405
|
+
// randBounded should also retry successfully (no revert = success)
|
|
406
|
+
euint256 a = e.randBounded(100);
|
|
407
|
+
processAllOperations();
|
|
408
|
+
|
|
409
|
+
// Verify we got a valid handle (non-zero)
|
|
410
|
+
assertTrue(euint256.unwrap(a) != bytes32(0), "Should return a valid handle");
|
|
411
|
+
|
|
412
|
+
// Verify fee was updated
|
|
413
|
+
bytes32 updatedFee = vm.load(address(this), FEE_SLOT);
|
|
414
|
+
assertEq(uint256(updatedFee), inco.getFee(), "Fee should be updated after retry");
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function testFeeRetryOnNewEuint256() public {
|
|
418
|
+
TakesEInput inputContract = new TakesEInput();
|
|
419
|
+
vm.deal(address(inputContract), 1 ether);
|
|
420
|
+
|
|
421
|
+
// First call to cache the fee
|
|
422
|
+
address self = address(this);
|
|
423
|
+
bytes memory ciphertext1 = fakePrepareEuint256Ciphertext(42, self, address(inputContract));
|
|
424
|
+
inputContract.setA(ciphertext1);
|
|
425
|
+
processAllOperations();
|
|
426
|
+
|
|
427
|
+
// Verify first value was stored correctly
|
|
428
|
+
assertEq(getUint256Value(inputContract.a()), 42, "First value should be 42");
|
|
429
|
+
|
|
430
|
+
// Set stale fee in the input contract
|
|
431
|
+
vm.store(address(inputContract), FEE_SLOT, bytes32(uint256(1)));
|
|
432
|
+
|
|
433
|
+
// Second call should retry and succeed
|
|
434
|
+
bytes memory ciphertext2 = fakePrepareEuint256Ciphertext(99, self, address(inputContract));
|
|
435
|
+
inputContract.setA(ciphertext2);
|
|
436
|
+
processAllOperations();
|
|
437
|
+
|
|
438
|
+
// Verify the retried value was stored correctly
|
|
439
|
+
assertEq(getUint256Value(inputContract.a()), 99, "Retried value should be 99");
|
|
440
|
+
|
|
441
|
+
// Verify fee was updated
|
|
442
|
+
bytes32 updatedFee = vm.load(address(inputContract), FEE_SLOT);
|
|
443
|
+
assertEq(uint256(updatedFee), inco.getFee(), "Fee should be updated after retry");
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function testFeeRetryOnNewEbool() public {
|
|
447
|
+
TakesEInput inputContract = new TakesEInput();
|
|
448
|
+
vm.deal(address(inputContract), 1 ether);
|
|
449
|
+
|
|
450
|
+
// First call to cache the fee
|
|
451
|
+
address self = address(this);
|
|
452
|
+
bytes memory ciphertext1 = fakePrepareEboolCiphertext(true, self, address(inputContract));
|
|
453
|
+
inputContract.setB(ciphertext1);
|
|
454
|
+
processAllOperations();
|
|
455
|
+
|
|
456
|
+
// Verify first value was stored correctly
|
|
457
|
+
assertEq(getBoolValue(inputContract.b()), true, "First value should be true");
|
|
458
|
+
|
|
459
|
+
// Set stale fee
|
|
460
|
+
vm.store(address(inputContract), FEE_SLOT, bytes32(uint256(1)));
|
|
461
|
+
|
|
462
|
+
// Second call should retry and succeed
|
|
463
|
+
bytes memory ciphertext2 = fakePrepareEboolCiphertext(false, self, address(inputContract));
|
|
464
|
+
inputContract.setB(ciphertext2);
|
|
465
|
+
processAllOperations();
|
|
466
|
+
|
|
467
|
+
// Verify the retried value was stored correctly
|
|
468
|
+
assertEq(getBoolValue(inputContract.b()), false, "Retried value should be false");
|
|
469
|
+
|
|
470
|
+
// Verify fee was updated
|
|
471
|
+
bytes32 updatedFee = vm.load(address(inputContract), FEE_SLOT);
|
|
472
|
+
assertEq(uint256(updatedFee), inco.getFee(), "Fee should be updated after retry");
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function testFeeRetryOnNewEaddress() public {
|
|
476
|
+
TakesEInput inputContract = new TakesEInput();
|
|
477
|
+
vm.deal(address(inputContract), 1 ether);
|
|
478
|
+
|
|
479
|
+
// First call to cache the fee
|
|
480
|
+
address self = address(this);
|
|
481
|
+
bytes memory ciphertext1 = fakePrepareEaddressCiphertext(alice, self, address(inputContract));
|
|
482
|
+
inputContract.setC(ciphertext1);
|
|
483
|
+
processAllOperations();
|
|
484
|
+
|
|
485
|
+
// Verify first value was stored correctly
|
|
486
|
+
assertEq(getAddressValue(inputContract.c()), alice, "First value should be alice");
|
|
487
|
+
|
|
488
|
+
// Set stale fee
|
|
489
|
+
vm.store(address(inputContract), FEE_SLOT, bytes32(uint256(1)));
|
|
490
|
+
|
|
491
|
+
// Second call should retry and succeed
|
|
492
|
+
bytes memory ciphertext2 = fakePrepareEaddressCiphertext(bob, self, address(inputContract));
|
|
493
|
+
inputContract.setC(ciphertext2);
|
|
494
|
+
processAllOperations();
|
|
495
|
+
|
|
496
|
+
// Verify the retried value was stored correctly
|
|
497
|
+
assertEq(getAddressValue(inputContract.c()), bob, "Retried value should be bob");
|
|
498
|
+
|
|
499
|
+
// Verify fee was updated
|
|
500
|
+
bytes32 updatedFee = vm.load(address(inputContract), FEE_SLOT);
|
|
501
|
+
assertEq(uint256(updatedFee), inco.getFee(), "Fee should be updated after retry");
|
|
502
|
+
}
|
|
503
|
+
|
|
241
504
|
function testEIfThenElse() public {
|
|
242
505
|
ebool controlA = e.asEbool(true);
|
|
243
506
|
ebool controlB = e.asEbool(false);
|