@inco/lightning 0.8.0-devnet-13 → 0.8.0-devnet-22

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.
Files changed (52) hide show
  1. package/manifest.yaml +66 -0
  2. package/package.json +8 -8
  3. package/src/IncoLightning.sol +5 -0
  4. package/src/Lib.alphanet.sol +31 -35
  5. package/src/Lib.demonet.sol +31 -35
  6. package/src/Lib.devnet.sol +31 -35
  7. package/src/Lib.sol +31 -35
  8. package/src/Lib.template.sol +54 -35
  9. package/src/Lib.testnet.sol +31 -35
  10. package/src/Types.sol +18 -0
  11. package/src/interfaces/IIncoLightning.sol +2 -0
  12. package/src/libs/incoLightning_alphanet_v0_297966649.sol +31 -35
  13. package/src/libs/incoLightning_alphanet_v1_725458969.sol +31 -35
  14. package/src/libs/incoLightning_alphanet_v2_976644394.sol +31 -35
  15. package/src/libs/incoLightning_demonet_v0_863421733.sol +31 -35
  16. package/src/libs/incoLightning_demonet_v2_467437523.sol +31 -35
  17. package/src/libs/incoLightning_devnet_v0_340846814.sol +31 -35
  18. package/src/libs/incoLightning_devnet_v1_904635675.sol +31 -35
  19. package/src/libs/incoLightning_devnet_v2_295237520.sol +31 -35
  20. package/src/libs/incoLightning_devnet_v3_976859633.sol +31 -35
  21. package/src/libs/incoLightning_devnet_v4_409204766.sol +31 -35
  22. package/src/libs/incoLightning_devnet_v5_203964628.sol +31 -35
  23. package/src/libs/incoLightning_devnet_v6_281949651.sol +31 -35
  24. package/src/libs/incoLightning_devnet_v7_24560427.sol +1197 -0
  25. package/src/libs/incoLightning_devnet_v8_985328058.sol +1197 -0
  26. package/src/libs/incoLightning_devnet_v9_269218568.sol +1197 -0
  27. package/src/libs/incoLightning_testnet_v0_183408998.sol +31 -35
  28. package/src/libs/incoLightning_testnet_v2_889158349.sol +31 -35
  29. package/src/lightning-parts/AccessControl/AdvancedAccessControl.sol +4 -3
  30. package/src/lightning-parts/AccessControl/BaseAccessControlList.sol +0 -16
  31. package/src/lightning-parts/AccessControl/interfaces/IAdvancedAccessControl.sol +1 -1
  32. package/src/lightning-parts/AccessControl/interfaces/IBaseAccessControlList.sol +0 -1
  33. package/src/lightning-parts/AccessControl/test/TestAdvancedAccessControl.t.sol +3 -1
  34. package/src/lightning-parts/AccessControl/test/TestBaseAccessControl.t.sol +0 -43
  35. package/src/lightning-parts/DecryptionAttester.sol +88 -2
  36. package/src/lightning-parts/DecryptionAttester.types.sol +9 -3
  37. package/src/lightning-parts/EList.sol +105 -25
  38. package/src/lightning-parts/EncryptedOperations.sol +18 -8
  39. package/src/lightning-parts/Fee.sol +29 -0
  40. package/src/lightning-parts/interfaces/IDecryptionAttester.sol +11 -2
  41. package/src/lightning-parts/interfaces/IEList.sol +11 -8
  42. package/src/lightning-parts/interfaces/IEncryptedInput.sol +2 -2
  43. package/src/lightning-parts/test/Elist.t.sol +160 -9
  44. package/src/lightning-parts/test/TestDecryptionAttestationInSynchronousFlow.t.sol +41 -13
  45. package/src/periphery/IncoUtils.sol +1 -1
  46. package/src/test/EListTester.sol +31 -13
  47. package/src/test/FakeIncoInfra/FakeComputeServer.sol +2 -2
  48. package/src/test/TestDeploy.t.sol +45 -1
  49. package/src/test/TestFakeInfra.t.sol +32 -6
  50. package/src/test/TestLib.t.sol +840 -19
  51. package/src/test/TestReceive.t.sol +42 -0
  52. package/src/version/IncoLightningConfig.sol +1 -1
@@ -3,11 +3,12 @@ pragma solidity ^0.8;
3
3
 
4
4
  import {IncoTest} from "../../test/IncoTest.sol";
5
5
  import {ElistTester} from "../../test/EListTester.sol";
6
- import {inco, e} from "../../Lib.sol";
7
- import {ETypes, elist} from "../../Types.sol";
8
- import {FEE, Fee} from "../Fee.sol";
9
- import {EList} from "../EList.sol";
6
+ import {ETypes, ListTooLong, elist, typeBitSize} from "../../Types.sol";
7
+ import {inco} from "../../Lib.sol";
8
+ import {FEE, BIT_FEE, Fee} from "../Fee.sol";
9
+ import {EList, MAX_LIST_LENGTH} from "../EList.sol";
10
10
  import {VerifierAddressGetter} from "../primitives/VerifierAddressGetter.sol";
11
+ import {stdError} from "forge-std/StdError.sol";
11
12
 
12
13
  contract ElistFeeTester is EList {
13
14
 
@@ -23,8 +24,38 @@ contract TestEList is IncoTest {
23
24
  function setUp() public virtual override {
24
25
  super.setUp();
25
26
  tester = new ElistTester(inco);
26
- vm.deal(address(tester), 1 ether);
27
+ vm.deal(address(tester), 100 ether);
28
+ vm.deal(address(this), 100 ether);
27
29
  feeTester = new ElistFeeTester();
30
+ vm.deal(address(feeTester), 100 ether);
31
+ }
32
+
33
+ function _listRange(uint16 start, uint16 end) internal returns (elist) {
34
+ return inco.listRange{value: uint256(end - start) * typeBitSize(ETypes.Uint256) * BIT_FEE}(start, end);
35
+ }
36
+
37
+ function _listAppend(elist list, bytes32 value) internal returns (elist) {
38
+ uint256 typeBits = typeBitSize(ETypes.Uint256);
39
+ return inco.listAppend{value: (uint256(inco.lengthOf(elist.unwrap(list))) * typeBits + typeBits) * BIT_FEE}(
40
+ list, value
41
+ );
42
+ }
43
+
44
+ function _listInsert(elist list, bytes32 idx, bytes32 val) internal returns (elist) {
45
+ uint256 typeBits = typeBitSize(ETypes.Uint256);
46
+ return inco.listInsert{value: (uint256(inco.lengthOf(elist.unwrap(list))) * typeBits + typeBits) * BIT_FEE}(
47
+ list, idx, val
48
+ );
49
+ }
50
+
51
+ function _listConcat(elist a, elist b) internal returns (elist) {
52
+ uint256 bitsA = uint256(inco.lengthOf(elist.unwrap(a))) * typeBitSize(ETypes.Uint256);
53
+ uint256 bitsB = uint256(inco.lengthOf(elist.unwrap(b))) * typeBitSize(ETypes.Uint256);
54
+ return inco.listConcat{value: (bitsA + bitsB) * BIT_FEE}(a, b);
55
+ }
56
+
57
+ function _newEList(bytes32[] memory handles, ETypes listType) internal returns (elist) {
58
+ return inco.newEList{value: uint256(handles.length) * typeBitSize(listType) * BIT_FEE}(handles, listType);
28
59
  }
29
60
 
30
61
  function testNewElistFromInputs() public {
@@ -47,21 +78,141 @@ contract TestEList is IncoTest {
47
78
  }
48
79
 
49
80
  function testRevertsOnBadFeeAmount() public {
81
+ // Construct a handle with length=1, type=Uint256 so fee = 1 * 256 * BIT_FEE
82
+ elist nonZeroList = elist.wrap(bytes32((uint256(1) << 24) | (uint256(uint8(ETypes.Uint256)) << 16)));
83
+ uint256 correctFee = 256 * BIT_FEE;
84
+
50
85
  // should fail if no fee
51
86
  vm.expectRevert(Fee.FeeNotPaid.selector);
52
- feeTester.listShuffle(elist.wrap(bytes32(0)));
87
+ feeTester.listShuffle(nonZeroList);
53
88
 
54
89
  // should fail if not enough fee
55
90
  vm.expectRevert(Fee.FeeNotPaid.selector);
56
- feeTester.listShuffle{value: FEE - 1}(elist.wrap(bytes32(0)));
91
+ feeTester.listShuffle{value: correctFee - 1}(nonZeroList);
57
92
 
58
93
  // should fail if too much fee
59
94
  vm.expectRevert(Fee.FeeNotPaid.selector);
60
- feeTester.listShuffle{value: FEE + 1}(elist.wrap(bytes32(0)));
95
+ feeTester.listShuffle{value: correctFee + 1}(nonZeroList);
61
96
 
97
+ // newEList(inputs): uses payingElistFee, so 3 * 256 * BIT_FEE needed, sending less
62
98
  vm.expectRevert(Fee.FeeNotPaid.selector);
63
99
  bytes[] memory inputs = new bytes[](3);
64
- feeTester.newEList{value: FEE * 2}(inputs, ETypes.Uint256, address(this));
100
+ feeTester.newEList{value: 3 * 256 * BIT_FEE - 1}(inputs, ETypes.Uint256, address(this));
101
+ }
102
+
103
+ function testListAppend_SucceedsAtMaxLength() public {
104
+ elist almostMaxList = _listRange(0, MAX_LIST_LENGTH - 1);
105
+
106
+ bytes32 validHandle = inco.listGet(almostMaxList, 0);
107
+
108
+ elist result = _listAppend(almostMaxList, validHandle);
109
+ assert(elist.unwrap(result) != bytes32(0));
110
+ }
111
+
112
+ function testListInsert_SucceedsAtMaxLength() public {
113
+ elist almostMaxList = _listRange(0, MAX_LIST_LENGTH - 1);
114
+
115
+ // Get valid handles from the list (listGet returns handles with transient permissions)
116
+ bytes32 validIndex = inco.listGet(almostMaxList, 0);
117
+ bytes32 validValue = inco.listGet(almostMaxList, 1);
118
+
119
+ elist result = _listInsert(almostMaxList, validIndex, validValue);
120
+ assert(elist.unwrap(result) != bytes32(0));
121
+ }
122
+
123
+ function testListConcat_SucceedsAtMaxLength() public {
124
+ uint16 half = MAX_LIST_LENGTH / 2;
125
+ uint16 otherHalf = MAX_LIST_LENGTH - half;
126
+ elist list1 = _listRange(0, half);
127
+ elist list2 = _listRange(0, otherHalf);
128
+
129
+ // Should succeed
130
+ elist combined = _listConcat(list1, list2);
131
+ assert(elist.unwrap(combined) != bytes32(0));
132
+ }
133
+
134
+ function testListRange_SucceedsAtMaxLength() public {
135
+ elist maxList = _listRange(0, MAX_LIST_LENGTH);
136
+ assert(elist.unwrap(maxList) != bytes32(0));
137
+ }
138
+
139
+ // ==================== Overflow Tests ====================
140
+ // These tests verify that exceeding MAX_LIST_LENGTH reverts appropriately.
141
+ // Operations use uint16 arithmetic which auto-reverts on overflow in Solidity 0.8+.
142
+
143
+ function testListAppend_RevertsOnOverflow() public {
144
+ elist maxList = _listRange(0, MAX_LIST_LENGTH);
145
+
146
+ bytes32 validHandle = inco.listGet(maxList, 0);
147
+
148
+ // Appending to a max-length list should revert with arithmetic overflow
149
+ // Fee: (65535*256 + 256) * BIT_FEE = 65536*32*FEE
150
+ uint256 typeBits = typeBitSize(ETypes.Uint256);
151
+ vm.expectRevert(stdError.arithmeticError);
152
+ inco.listAppend{value: (uint256(MAX_LIST_LENGTH) * typeBits + typeBits) * BIT_FEE}(maxList, validHandle);
153
+ }
154
+
155
+ function testListInsert_RevertsOnOverflow() public {
156
+ elist maxList = _listRange(0, MAX_LIST_LENGTH);
157
+
158
+ bytes32 validIndex = inco.listGet(maxList, 0);
159
+ bytes32 validValue = inco.listGet(maxList, 1);
160
+
161
+ // Inserting into a max-length list should revert with arithmetic overflow
162
+ uint256 typeBits = typeBitSize(ETypes.Uint256);
163
+ vm.expectRevert(stdError.arithmeticError);
164
+ inco.listInsert{value: (uint256(MAX_LIST_LENGTH) * typeBits + typeBits) * BIT_FEE}(
165
+ maxList, validIndex, validValue
166
+ );
167
+ }
168
+
169
+ function testListConcat_RevertsOnOverflow() public {
170
+ uint16 half = MAX_LIST_LENGTH / 2 + 1;
171
+ elist list1 = _listRange(0, half);
172
+ elist list2 = _listRange(0, half);
173
+
174
+ // Concatenating should revert with arithmetic overflow
175
+ uint256 bitsPerList = uint256(half) * typeBitSize(ETypes.Uint256);
176
+ vm.expectRevert(stdError.arithmeticError);
177
+ inco.listConcat{value: (bitsPerList + bitsPerList) * BIT_FEE}(list1, list2);
178
+ }
179
+
180
+ function testNewEList_RevertsOnTooManyHandles() public {
181
+ uint256 tooMany = uint256(MAX_LIST_LENGTH) + 1;
182
+ bytes32[] memory handles = new bytes32[](tooMany);
183
+
184
+ // Fee check runs before length check, so we must pay the required fee
185
+ uint256 requiredFee = tooMany * typeBitSize(ETypes.Uint256) * BIT_FEE;
186
+
187
+ // Should revert with ListTooLong error
188
+ vm.expectRevert(abi.encodeWithSelector(ListTooLong.selector, uint32(tooMany), MAX_LIST_LENGTH));
189
+ inco.newEList{value: requiredFee}(handles, ETypes.Uint256);
190
+ }
191
+
192
+ function testNewEList_RevertsOnTooManyInputs() public {
193
+ uint256 tooMany = uint256(MAX_LIST_LENGTH) + 1;
194
+ bytes[] memory inputs = new bytes[](tooMany);
195
+
196
+ // Fee check runs before length check, so we must pay the required fee
197
+ uint256 requiredFee = tooMany * typeBitSize(ETypes.Uint256) * BIT_FEE;
198
+
199
+ // Should revert with ListTooLong error
200
+ vm.expectRevert(abi.encodeWithSelector(ListTooLong.selector, uint32(tooMany), MAX_LIST_LENGTH));
201
+ inco.newEList{value: requiredFee}(inputs, ETypes.Uint256, address(this));
202
+ }
203
+
204
+ function testNewEListFromHandles() public {
205
+ elist sourceList = _listRange(0, 3);
206
+
207
+ // Extract handles from the source list
208
+ bytes32[] memory handles = new bytes32[](3);
209
+ handles[0] = inco.listGet(sourceList, 0);
210
+ handles[1] = inco.listGet(sourceList, 1);
211
+ handles[2] = inco.listGet(sourceList, 2);
212
+
213
+ // Create a new list from these handles
214
+ elist newList = _newEList(handles, ETypes.Uint256);
215
+ assert(elist.unwrap(newList) != bytes32(0));
65
216
  }
66
217
 
67
218
  }
@@ -2,21 +2,44 @@
2
2
  pragma solidity ^0.8;
3
3
 
4
4
  import {IncoTest} from "../../test/IncoTest.sol";
5
- import {DemoToken} from "@inco/confidential-token-demo/src/DemoToken.sol";
5
+ import {ERC7984DemoToken} from "@inco/incoERC7984/src/ERC7984DemoToken.sol";
6
6
  import {DecryptionAttestation} from "../DecryptionAttester.types.sol";
7
7
  import {GWEI} from "../../shared/TypeUtils.sol";
8
- import {euint256} from "@inco/lightning/src/Lib.sol"; // import via remapping or compiler fails
8
+ import {inco, e, euint256} from "@inco/lightning/src/Lib.sol"; // import via remapping or compiler fails
9
9
  import {AllowanceProof} from "../AccessControl/AdvancedAccessControl.sol";
10
- import {inco} from "../../Lib.sol";
11
10
  import {euint256 as remappedEuint256} from "@inco/lightning/src/Lib.sol";
11
+ import {
12
+ DecryptionAttestation as remappedDecryptionAttestation
13
+ } from "@inco/lightning/src/lightning-parts/DecryptionAttester.types.sol";
12
14
 
13
- contract TokenBurnCurrentBalance is DemoToken {
15
+ /// @notice This test demonstrates how a decryption attestation can be used in a synchronous flow, where the result of the operation is known at the time of requesting the attestation.
16
+ /// In this example, we simulate a token burn operation where the user proves that they have enough balance to burn before actually performing the burn.
17
+ contract TokenBurnCurrentBalance is ERC7984DemoToken {
14
18
 
15
- function burnFullCurrentBalance(DecryptionAttestation memory decryption, bytes[] memory signatures) public {
19
+ using e for uint256;
20
+ using e for euint256;
21
+
22
+ /// @notice Deploys the token with specified name, symbol, and initial supply
23
+ /// @param name_ The token name
24
+ /// @param symbol_ The token symbol
25
+ /// @param initialSupply The initial supply to mint to the deployer (in base units)
26
+ constructor(string memory name_, string memory symbol_, uint256 initialSupply)
27
+ ERC7984DemoToken(name_, symbol_, initialSupply)
28
+ {
29
+ if (initialSupply > 0) {
30
+ _mint(msg.sender, initialSupply.asEuint256());
31
+ }
32
+ }
33
+
34
+ /// @dev In this burn function, the user provides a decryption attestation that proves they have enough balance to burn the specified amount.
35
+ /// The burn operation is performed synchronously, and the success of the burn is verified using the attestation.
36
+ function burnFullCurrentBalance(remappedDecryptionAttestation memory attestation, bytes[] memory signatures)
37
+ public
38
+ {
16
39
  euint256 currentBalance = confidentialBalanceOf(msg.sender);
17
- require(inco.incoVerifier().isValidDecryptionAttestation(decryption, signatures), "Invalid Signature");
18
- require(euint256.unwrap(currentBalance) == decryption.handle, "Handle mismatch");
19
- publicBurn(msg.sender, uint256(decryption.value));
40
+ require(inco.incoVerifier().isValidDecryptionAttestation(attestation, signatures), "Invalid Signature");
41
+ require(euint256.unwrap(currentBalance) == attestation.handle, "Handle mismatch");
42
+ publicBurn(msg.sender, uint256(attestation.value));
20
43
  }
21
44
 
22
45
  }
@@ -26,19 +49,24 @@ contract TestDecryptionAttestationInSynchronousFlow is IncoTest {
26
49
  AllowanceProof emptyProof; // no proof needed when requester has the handle in persisted allowed pairs
27
50
 
28
51
  function testSynchronousBurning() public {
29
- TokenBurnCurrentBalance token = new TokenBurnCurrentBalance();
52
+ TokenBurnCurrentBalance token = new TokenBurnCurrentBalance("DemoToken", "DMT", 100 ether);
30
53
  vm.deal(address(token), 100 ether);
31
- token.confidentialTransfer(alice, fakePrepareEuint256Ciphertext(10 * GWEI, address(this), address(token)), "");
54
+ token.confidentialTransfer(alice, fakePrepareEuint256Ciphertext(10 * GWEI, address(this), address(token)));
32
55
  processAllOperations(); // saves Alice's balance
56
+
33
57
  bytes32 aliceCurrentBalanceHandle = euint256.unwrap(token.confidentialBalanceOf(alice));
34
58
  // simulates Alice requesting for a decryption attestation of Ge op on her balance and the amount
35
59
  // she intends to burn, therefore proving to the token contract that the operation will succeed
36
- (DecryptionAttestation memory decryption, bytes[] memory signatures) =
60
+ (DecryptionAttestation memory attestation, bytes[] memory signatures) =
37
61
  getDecryptionAttestation(alice, HandleWithProof({handle: aliceCurrentBalanceHandle, proof: emptyProof}));
38
- vm.prank(alice);
39
62
 
63
+ // Convert attestation to remapped type for cross-project compatibility
64
+ remappedDecryptionAttestation memory remappedAttestation =
65
+ remappedDecryptionAttestation({handle: attestation.handle, value: attestation.value});
66
+
67
+ vm.prank(alice);
40
68
  // the decryption attestation is passed to the token burn method
41
- token.burnFullCurrentBalance(decryption, signatures);
69
+ token.burnFullCurrentBalance(remappedAttestation, signatures);
42
70
 
43
71
  processAllOperations();
44
72
 
@@ -5,7 +5,7 @@ import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol";
5
5
 
6
6
  // Re-export FEE constant for convenience - consumers can import both IncoUtils and FEE from this file
7
7
  // forge-lint: disable-next-line(unused-import)
8
- import {FEE} from "../lightning-parts/Fee.sol";
8
+ import {FEE, BIT_FEE} from "../lightning-parts/Fee.sol";
9
9
 
10
10
  contract IncoUtils {
11
11
 
@@ -3,9 +3,9 @@ pragma solidity ^0.8;
3
3
 
4
4
  import {IIncoLightning} from "../interfaces/IIncoLightning.sol";
5
5
  /// forge-lint: disable-next-line(unused-import)
6
- import {ETypes, elist} from "../Types.sol";
6
+ import {ETypes, elist, typeBitSize} from "../Types.sol";
7
7
  import {euint256} from "../Types.sol";
8
- import {IncoUtils, FEE} from "../periphery/IncoUtils.sol";
8
+ import {IncoUtils, FEE, BIT_FEE} from "../periphery/IncoUtils.sol";
9
9
 
10
10
  contract ElistTester is IncoUtils {
11
11
 
@@ -35,7 +35,10 @@ contract ElistTester is IncoUtils {
35
35
  euint256 handle = inco.newEuint256{value: FEE}(ctValue, msg.sender);
36
36
  inco.allow(euint256.unwrap(handle), address(this));
37
37
  inco.allow(euint256.unwrap(handle), address(msg.sender));
38
- list = inco.listAppend(list, euint256.unwrap(handle));
38
+ uint256 typeBits = typeBitSize(ETypes(uint8(uint256(elist.unwrap(list)) >> 16)));
39
+ list = inco.listAppend{value: (uint256(inco.lengthOf(elist.unwrap(list))) * typeBits + typeBits) * BIT_FEE}(
40
+ list, euint256.unwrap(handle)
41
+ );
39
42
  inco.allow(elist.unwrap(list), address(this));
40
43
  inco.allow(elist.unwrap(list), address(msg.sender));
41
44
  return list;
@@ -48,7 +51,7 @@ contract ElistTester is IncoUtils {
48
51
  }
49
52
 
50
53
  function newEList(bytes32[] memory handles, ETypes listType) public payable refundUnspent returns (elist) {
51
- list = inco.newEList(handles, listType);
54
+ list = inco.newEList{value: FEE * handles.length}(handles, listType);
52
55
  inco.allow(elist.unwrap(list), address(this));
53
56
  inco.allow(elist.unwrap(list), address(msg.sender));
54
57
  return list;
@@ -70,7 +73,10 @@ contract ElistTester is IncoUtils {
70
73
  function listSet(bytes memory ctIndex, bytes memory ctValue) public payable refundUnspent returns (elist) {
71
74
  euint256 index = inco.newEuint256{value: FEE}(ctIndex, msg.sender);
72
75
  euint256 value = inco.newEuint256{value: FEE}(ctValue, msg.sender);
73
- list = inco.listSet(list, euint256.unwrap(index), euint256.unwrap(value));
76
+ uint256 typeBits = typeBitSize(ETypes(uint8(uint256(elist.unwrap(list)) >> 16)));
77
+ list = inco.listSet{value: uint256(inco.lengthOf(elist.unwrap(list))) * typeBits * BIT_FEE}(
78
+ list, euint256.unwrap(index), euint256.unwrap(value)
79
+ );
74
80
  inco.allow(elist.unwrap(list), address(this));
75
81
  inco.allow(elist.unwrap(list), address(msg.sender));
76
82
  return list;
@@ -79,7 +85,10 @@ contract ElistTester is IncoUtils {
79
85
  function listInsert(bytes memory ctIndex, bytes memory ctValue) public payable refundUnspent returns (elist) {
80
86
  euint256 index = inco.newEuint256{value: FEE}(ctIndex, msg.sender);
81
87
  euint256 value = inco.newEuint256{value: FEE}(ctValue, msg.sender);
82
- list = inco.listInsert(list, euint256.unwrap(index), euint256.unwrap(value));
88
+ uint256 typeBits = typeBitSize(ETypes(uint8(uint256(elist.unwrap(list)) >> 16)));
89
+ list = inco.listInsert{value: (uint256(inco.lengthOf(elist.unwrap(list))) * typeBits + typeBits) * BIT_FEE}(
90
+ list, euint256.unwrap(index), euint256.unwrap(value)
91
+ );
83
92
  inco.allow(elist.unwrap(list), address(this));
84
93
  inco.allow(elist.unwrap(list), address(msg.sender));
85
94
  return list;
@@ -94,7 +103,11 @@ contract ElistTester is IncoUtils {
94
103
  elist rhs = inco.newEList{value: FEE * cts.length}(cts, listType, user);
95
104
  inco.allow(elist.unwrap(rhs), address(this));
96
105
  inco.allow(elist.unwrap(rhs), address(msg.sender));
97
- list = inco.listConcat(list, rhs);
106
+ uint256 lhsBits =
107
+ uint256(inco.lengthOf(elist.unwrap(list))) * typeBitSize(ETypes(uint8(uint256(elist.unwrap(list)) >> 16)));
108
+ uint256 rhsBits =
109
+ uint256(inco.lengthOf(elist.unwrap(rhs))) * typeBitSize(ETypes(uint8(uint256(elist.unwrap(rhs)) >> 16)));
110
+ list = inco.listConcat{value: (lhsBits + rhsBits) * BIT_FEE}(list, rhs);
98
111
  inco.allow(elist.unwrap(list), address(this));
99
112
  inco.allow(elist.unwrap(list), address(msg.sender));
100
113
  return list;
@@ -108,14 +121,17 @@ contract ElistTester is IncoUtils {
108
121
  {
109
122
  euint256 start = inco.newEuint256{value: FEE}(ctStart, msg.sender);
110
123
  euint256 defaultValue = inco.newEuint256{value: FEE}(ctDefaultValue, msg.sender);
111
- list = inco.listSlice(list, euint256.unwrap(start), len, euint256.unwrap(defaultValue));
124
+ uint256 typeBits = typeBitSize(ETypes(uint8(uint256(elist.unwrap(list)) >> 16)));
125
+ list = inco.listSlice{value: uint256(len) * typeBits * BIT_FEE}(
126
+ list, euint256.unwrap(start), len, euint256.unwrap(defaultValue)
127
+ );
112
128
  inco.allow(elist.unwrap(list), address(this));
113
129
  inco.allow(elist.unwrap(list), address(msg.sender));
114
130
  return list;
115
131
  }
116
132
 
117
- function listRange(uint16 start, uint16 end) public returns (elist) {
118
- newRangeList = inco.listRange(start, end);
133
+ function listRange(uint16 start, uint16 end) public payable returns (elist) {
134
+ newRangeList = inco.listRange{value: uint256(end - start) * typeBitSize(ETypes.Uint256) * BIT_FEE}(start, end);
119
135
  inco.allow(elist.unwrap(newRangeList), address(this));
120
136
  inco.allow(elist.unwrap(newRangeList), address(msg.sender));
121
137
  return newRangeList;
@@ -128,14 +144,16 @@ contract ElistTester is IncoUtils {
128
144
  }
129
145
 
130
146
  function listShuffle() public payable refundUnspent returns (elist) {
131
- list = inco.listShuffle{value: FEE}(list);
147
+ uint256 typeBits = typeBitSize(ETypes(uint8(uint256(elist.unwrap(list)) >> 16)));
148
+ list = inco.listShuffle{value: uint256(inco.lengthOf(elist.unwrap(list))) * typeBits * BIT_FEE}(list);
132
149
  inco.allow(elist.unwrap(list), address(this));
133
150
  inco.allow(elist.unwrap(list), address(msg.sender));
134
151
  return list;
135
152
  }
136
153
 
137
- function listReverse() public returns (elist) {
138
- list = inco.listReverse(list);
154
+ function listReverse() public payable returns (elist) {
155
+ uint256 typeBits = typeBitSize(ETypes(uint8(uint256(elist.unwrap(list)) >> 16)));
156
+ list = inco.listReverse{value: uint256(inco.lengthOf(elist.unwrap(list))) * typeBits * BIT_FEE}(list);
139
157
  inco.allow(elist.unwrap(list), address(this));
140
158
  inco.allow(elist.unwrap(list), address(msg.sender));
141
159
  return list;
@@ -14,9 +14,9 @@ contract FakeComputeServer {
14
14
  } else if (op == EOps.Mul) {
15
15
  return lhs * rhs;
16
16
  } else if (op == EOps.Div) {
17
- return lhs / rhs;
17
+ return rhs == 0 ? type(uint256).max : lhs / rhs;
18
18
  } else if (op == EOps.Rem) {
19
- return lhs % rhs;
19
+ return rhs == 0 ? lhs : lhs % rhs;
20
20
  } else if (op == EOps.Min) {
21
21
  return lhs < rhs ? lhs : rhs;
22
22
  } else if (op == EOps.Max) {
@@ -1,13 +1,14 @@
1
1
  // SPDX-License-Identifier: No License
2
2
  pragma solidity ^0.8;
3
3
 
4
- import {Test} from "forge-std/Test.sol";
4
+ import {Test, Vm} from "forge-std/Test.sol";
5
5
  import {TrivialEncryption} from "../lightning-parts/TrivialEncryption.sol";
6
6
  import {ETypes} from "../Types.sol";
7
7
  import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
8
8
  import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
9
9
  import {inco} from "../Lib.sol";
10
10
  import {IncoTest} from "./IncoTest.sol";
11
+ import {IEncryptedInput} from "../lightning-parts/interfaces/IEncryptedInput.sol";
11
12
 
12
13
  contract ReturnTwo is UUPSUpgradeable {
13
14
 
@@ -44,6 +45,26 @@ contract TestDeploy is Test, IncoTest {
44
45
  assertTrue(inco.isAcceptedVersion(42));
45
46
  }
46
47
 
48
+ function testAddAcceptedVersion_EmitsVersionAccepted() public {
49
+ vm.recordLogs();
50
+ vm.prank(owner);
51
+ inco.addAcceptedVersion(42);
52
+
53
+ Vm.Log[] memory logs = vm.getRecordedLogs();
54
+ bytes32 expectedTopic0 = keccak256("VersionAccepted(uint16)");
55
+ bool found = false;
56
+ for (uint256 i = 0; i < logs.length; i++) {
57
+ if (logs[i].topics[0] == expectedTopic0) {
58
+ found = true;
59
+ // version is indexed: must appear in topics[1], not in data
60
+ assertEq(logs[i].topics[1], bytes32(uint256(42)), "version should be an indexed topic");
61
+ assertEq(logs[i].data.length, 0, "data should be empty since version is indexed");
62
+ break;
63
+ }
64
+ }
65
+ assertTrue(found, "VersionAccepted event was not emitted");
66
+ }
67
+
47
68
  function testAddAcceptedVersionNotOwner() public {
48
69
  vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, alice));
49
70
  vm.prank(alice);
@@ -57,6 +78,29 @@ contract TestDeploy is Test, IncoTest {
57
78
  assertFalse(inco.isAcceptedVersion(42));
58
79
  }
59
80
 
81
+ function testRemoveAcceptedVersion_EmitsVersionRemoved() public {
82
+ vm.prank(owner);
83
+ inco.addAcceptedVersion(42);
84
+
85
+ vm.recordLogs();
86
+ vm.prank(owner);
87
+ inco.removeAcceptedVersion(42);
88
+
89
+ Vm.Log[] memory logs = vm.getRecordedLogs();
90
+ bytes32 expectedTopic0 = keccak256("VersionRemoved(uint16)");
91
+ bool found = false;
92
+ for (uint256 i = 0; i < logs.length; i++) {
93
+ if (logs[i].topics[0] == expectedTopic0) {
94
+ found = true;
95
+ // version is indexed: must appear in topics[1], not in data
96
+ assertEq(logs[i].topics[1], bytes32(uint256(42)), "version should be an indexed topic");
97
+ assertEq(logs[i].data.length, 0, "data should be empty since version is indexed");
98
+ break;
99
+ }
100
+ }
101
+ assertTrue(found, "VersionRemoved event was not emitted");
102
+ }
103
+
60
104
  function testRemoveAcceptedVersionNotOwner() public {
61
105
  vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, alice));
62
106
  vm.prank(alice);
@@ -3,7 +3,7 @@ pragma solidity ^0.8;
3
3
 
4
4
  import {IncoTest} from "./IncoTest.sol";
5
5
  import {e, euint256, ebool, eaddress, inco} from "../Lib.sol";
6
- import {SenderNotAllowedForHandle} from "../Types.sol";
6
+ import {SenderNotAllowedForHandle, UnsupportedType, ETypes, UnexpectedType, typeToBitMask} from "../Types.sol";
7
7
  import {TEELifecycle} from "../lightning-parts/TEELifecycle.sol";
8
8
  import {Td10ReportBody, MINIMUM_QUOTE_LENGTH} from "../interfaces/automata-interfaces/Types.sol";
9
9
 
@@ -96,6 +96,14 @@ contract TestFakeInfra is IncoTest {
96
96
  assertEq(getUint256Value(c), 2);
97
97
  }
98
98
 
99
+ function testEDiv_DivisionByZeroReturnsMaxUint256() public {
100
+ euint256 a = e.asEuint256(10);
101
+ euint256 zero = e.asEuint256(0);
102
+ euint256 c = a.div(zero);
103
+ processAllOperations();
104
+ assertEq(getUint256Value(c), type(uint256).max);
105
+ }
106
+
99
107
  function testERem() public {
100
108
  euint256 a = e.asEuint256(10);
101
109
  euint256 b = e.asEuint256(4);
@@ -104,6 +112,14 @@ contract TestFakeInfra is IncoTest {
104
112
  assertEq(getUint256Value(c), 2);
105
113
  }
106
114
 
115
+ function testERem_RemainderByZeroReturnsLhs() public {
116
+ euint256 a = e.asEuint256(10);
117
+ euint256 zero = e.asEuint256(0);
118
+ euint256 c = a.rem(zero);
119
+ processAllOperations();
120
+ assertEq(getUint256Value(c), 10);
121
+ }
122
+
107
123
  function testEAnd() public {
108
124
  euint256 a = e.asEuint256(10);
109
125
  euint256 b = e.asEuint256(4);
@@ -514,11 +530,10 @@ contract TestFakeInfra is IncoTest {
514
530
  assertEq(getUint256Value(resB), 4);
515
531
  }
516
532
 
517
- function testECastToEBool() public {
533
+ function testECastToEBoolRevert() public {
518
534
  euint256 a = e.asEuint256(1);
519
- ebool b = a.asEbool();
520
- processAllOperations();
521
- assertEq(getBoolValue(b), true);
535
+ vm.expectRevert(abi.encodeWithSelector(UnsupportedType.selector, ETypes.Bool));
536
+ inco.eCast(euint256.unwrap(a), ETypes.Bool);
522
537
  }
523
538
 
524
539
  function testECastToFromEBool() public {
@@ -557,7 +572,7 @@ contract TestFakeInfra is IncoTest {
557
572
  function testECastAllowed() public {
558
573
  bytes32 invalidHandle = keccak256("invalid handle");
559
574
  vm.expectRevert(abi.encodeWithSelector(SenderNotAllowedForHandle.selector, invalidHandle, address(this)));
560
- euint256.wrap(invalidHandle).asEbool();
575
+ ebool.wrap(invalidHandle).asEuint256();
561
576
  }
562
577
 
563
578
  function testUninitializedHandleIsDisallowed_Add_Lhs() public {
@@ -1104,4 +1119,15 @@ contract TestFakeInfra is IncoTest {
1104
1119
  assertEq(quote.length, MINIMUM_QUOTE_LENGTH);
1105
1120
  }
1106
1121
 
1122
+ function testRandBoundedUpperBoundLargerThanRandType() public {
1123
+ euint256 upperBound = e.asEuint256(type(uint256).max);
1124
+ uint256 fee = inco.getFee();
1125
+ vm.expectRevert(
1126
+ abi.encodeWithSelector(
1127
+ UnexpectedType.selector, ETypes.Uint256, typeToBitMask(ETypes.AddressOrUint160OrBytes20)
1128
+ )
1129
+ );
1130
+ inco.eRandBounded{value: fee}(euint256.unwrap(upperBound), ETypes.AddressOrUint160OrBytes20);
1131
+ }
1132
+
1107
1133
  }