@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.
Files changed (34) hide show
  1. package/manifest.yaml +22 -0
  2. package/package.json +1 -1
  3. package/src/IncoLightning.sol +4 -0
  4. package/src/Lib.alphanet.sol +62 -6
  5. package/src/Lib.demonet.sol +62 -6
  6. package/src/Lib.devnet.sol +63 -7
  7. package/src/Lib.sol +63 -7
  8. package/src/Lib.template.sol +65 -9
  9. package/src/Lib.testnet.sol +62 -6
  10. package/src/interfaces/IIncoLightning.sol +2 -0
  11. package/src/libs/incoLightning_alphanet_v0_297966649.sol +62 -6
  12. package/src/libs/incoLightning_alphanet_v1_725458969.sol +62 -6
  13. package/src/libs/incoLightning_alphanet_v2_976644394.sol +62 -6
  14. package/src/libs/incoLightning_demonet_v0_863421733.sol +62 -6
  15. package/src/libs/incoLightning_demonet_v2_467437523.sol +62 -6
  16. package/src/libs/incoLightning_devnet_v0_340846814.sol +62 -6
  17. package/src/libs/incoLightning_devnet_v1_904635675.sol +62 -6
  18. package/src/libs/incoLightning_devnet_v2_295237520.sol +62 -6
  19. package/src/libs/incoLightning_devnet_v3_976859633.sol +534 -0
  20. package/src/libs/incoLightning_testnet_v0_183408998.sol +62 -6
  21. package/src/libs/incoLightning_testnet_v2_889158349.sol +62 -6
  22. package/src/lightning-parts/Fee.sol +10 -14
  23. package/src/lightning-parts/TEELifecycle.sol +11 -12
  24. package/src/lightning-parts/TEELifecycle.types.sol +1 -4
  25. package/src/periphery/IncoUtils.sol +42 -0
  26. package/src/test/AddTwo.sol +3 -3
  27. package/src/test/FakeIncoInfra/MockRemoteAttestation.sol +2 -3
  28. package/src/test/IncoTest.sol +2 -2
  29. package/src/test/TEELifecycle/TEELifecycleMockTest.t.sol +1 -1
  30. package/src/test/TestFakeInfra.t.sol +263 -0
  31. package/src/test/TestFeeWithdrawal.t.sol +60 -0
  32. package/src/test/TestIncoUtils.t.sol +242 -0
  33. package/src/version/IncoLightningConfig.sol +2 -2
  34. package/src/IIncoLightning.sol +0 -22
@@ -0,0 +1,60 @@
1
+ // SPDX-License-Identifier: No License
2
+ pragma solidity ^0.8;
3
+
4
+ import {inco} from "../Lib.sol";
5
+ import {IncoTest} from "./IncoTest.sol";
6
+ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
7
+ import {Fee} from "../lightning-parts/Fee.sol";
8
+
9
+ contract RejectingContract {
10
+ // Contract that rejects all Ether transfers
11
+
12
+ }
13
+
14
+ contract TestFeeWithdrawal is IncoTest {
15
+
16
+ function setUp() public override {
17
+ super.setUp();
18
+ }
19
+
20
+ function testWithdrawFees_Succeeds() public {
21
+ vm.deal(address(inco), 1 ether);
22
+ vm.prank(owner);
23
+ inco.withdrawFees();
24
+ assertEq(address(inco).balance, 0);
25
+ assertEq(address(owner).balance, 1 ether);
26
+ }
27
+
28
+ function testWithdrawFees_Reverts_NoFeesToWithdraw() public {
29
+ vm.expectRevert(Fee.NoFeesToWithdraw.selector);
30
+ vm.prank(owner);
31
+ inco.withdrawFees();
32
+ }
33
+
34
+ function testWithdrawFees_Reverts_NotOwner() public {
35
+ vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, alice));
36
+ vm.prank(alice);
37
+ inco.withdrawFees();
38
+ }
39
+
40
+ function testWithdrawFees_Reverts_FeeWithdrawalFailed() public {
41
+ // Deploy a contract that rejects Ether
42
+ RejectingContract rejectingOwner = new RejectingContract();
43
+
44
+ // Change owner to the rejecting contract
45
+ vm.prank(owner);
46
+ inco.transferOwnership(address(rejectingOwner));
47
+
48
+ // Add fees to withdraw
49
+ vm.deal(address(inco), 1 ether);
50
+
51
+ // Expect the FeeWithdrawalFailed error
52
+ vm.expectRevert(Fee.FeeWithdrawalFailed.selector);
53
+
54
+ // Try to withdraw (will fail because rejectingOwner can't receive Ether)
55
+ vm.prank(address(rejectingOwner));
56
+ inco.withdrawFees();
57
+ }
58
+
59
+ }
60
+
@@ -0,0 +1,242 @@
1
+ // SPDX-License-Identifier: No License
2
+ pragma solidity ^0.8;
3
+
4
+ import {Test} from "forge-std/Test.sol";
5
+ import {IncoUtils} from "../periphery/IncoUtils.sol";
6
+
7
+ /// @dev Helper contract that inherits IncoUtils and rejects ETH refunds
8
+ contract RejectingContract is IncoUtils {
9
+
10
+ // No receive() function - will reject ETH transfers
11
+
12
+ function callWithRefund() external payable refundUnspent {
13
+ // Does nothing, should trigger refund which will fail because this contract can't receive ETH
14
+ }
15
+
16
+ }
17
+
18
+ /// @dev Helper contract that sends ETH to the target during execution
19
+ contract EthSenderDuringExecution {
20
+
21
+ function sendTo(address target, uint256 amount) external {
22
+ payable(target).transfer(amount);
23
+ }
24
+
25
+ receive() external payable {}
26
+
27
+ }
28
+
29
+ /// @dev Attacker contract that tries to re-enter on receiving refund
30
+ contract ReentrantAttacker {
31
+
32
+ TestIncoUtils target;
33
+ uint256 public attackCount;
34
+
35
+ constructor(TestIncoUtils _target) {
36
+ target = _target;
37
+ }
38
+
39
+ function attack() external payable {
40
+ target.mockFunctionNoSpend{value: msg.value}();
41
+ }
42
+
43
+ receive() external payable {
44
+ attackCount++;
45
+ if (attackCount < 3) {
46
+ // Try to re-enter
47
+ target.mockFunctionNoSpend{value: msg.value}();
48
+ }
49
+ }
50
+
51
+ }
52
+
53
+ contract TestIncoUtils is Test, IncoUtils {
54
+
55
+ EthSenderDuringExecution sender;
56
+
57
+ function setUp() public {
58
+ sender = new EthSenderDuringExecution();
59
+ }
60
+
61
+ // Mock functions to test the modifier
62
+ function mockFunctionNoSpend() external payable refundUnspent {
63
+ // Does nothing, no ETH spent
64
+ }
65
+
66
+ function mockFunctionPartialSpend(uint256 amount) external payable refundUnspent {
67
+ // Send ETH to simulate spending
68
+ payable(address(0)).transfer(amount);
69
+ }
70
+
71
+ function mockFunctionFullSpend() external payable refundUnspent {
72
+ // Send all msg.value to simulate full spending
73
+ payable(address(0)).transfer(msg.value);
74
+ }
75
+
76
+ /// @dev Mock function that receives ETH during execution (simulates external deposit)
77
+ function mockFunctionReceivesEthDuringExecution(address payable ethSender, uint256 incomingAmount)
78
+ external
79
+ payable
80
+ refundUnspent
81
+ {
82
+ // Trigger external contract to send us ETH during execution
83
+ EthSenderDuringExecution(ethSender).sendTo(address(this), incomingAmount);
84
+ }
85
+
86
+ receive() external payable {}
87
+
88
+ function testRefundUnspentWhenNoSpend() public {
89
+ uint256 msgValue = 1 ether;
90
+
91
+ // Set up balance for the call (vm.deal overwrites, not adds)
92
+ vm.deal(address(this), msgValue);
93
+ uint256 balanceBefore = address(this).balance;
94
+
95
+ // Call function that doesn't spend any ETH
96
+ this.mockFunctionNoSpend{value: msgValue}();
97
+
98
+ // Check that full amount was refunded (balance unchanged)
99
+ assertEq(address(this).balance, balanceBefore);
100
+ }
101
+
102
+ function testRefundUnspentWhenPartialSpend() public {
103
+ uint256 msgValue = 1 ether;
104
+ uint256 spendAmount = 0.3 ether;
105
+
106
+ // Set up balance for the call
107
+ vm.deal(address(this), msgValue);
108
+ uint256 balanceBefore = address(this).balance;
109
+
110
+ // Call function that spends partial amount
111
+ this.mockFunctionPartialSpend{value: msgValue}(spendAmount);
112
+
113
+ // Check that correct amount was refunded (only spendAmount was consumed)
114
+ assertEq(address(this).balance, balanceBefore - spendAmount);
115
+ }
116
+
117
+ function testRefundUnspentWhenFullSpend() public {
118
+ uint256 msgValue = 1 ether;
119
+
120
+ // Set up balance for the call
121
+ vm.deal(address(this), msgValue);
122
+ uint256 balanceBefore = address(this).balance;
123
+
124
+ // Call function that spends full amount
125
+ this.mockFunctionFullSpend{value: msgValue}();
126
+
127
+ // Check that no refund occurred (all was spent)
128
+ assertEq(address(this).balance, balanceBefore - msgValue);
129
+ }
130
+
131
+ function testRefundUnspentWhenOverSpend() public {
132
+ uint256 msgValue = 0.5 ether;
133
+ uint256 spendAmount = 1 ether;
134
+
135
+ // Add extra balance to contract beyond what we'll send
136
+ vm.deal(address(this), spendAmount + msgValue);
137
+ uint256 balanceBefore = address(this).balance;
138
+
139
+ // Call function that spends more than msg.value (uses contract's existing balance)
140
+ this.mockFunctionPartialSpend{value: msgValue}(spendAmount);
141
+
142
+ // Check that no refund occurred (spent more than msg.value, so nothing to refund)
143
+ assertEq(address(this).balance, balanceBefore - spendAmount);
144
+ }
145
+
146
+ function testRefundFailedReverts() public {
147
+ uint256 msgValue = 1 ether;
148
+
149
+ // Create a contract that cannot receive ETH refunds (no receive function)
150
+ RejectingContract rejecter = new RejectingContract();
151
+ vm.deal(address(rejecter), msgValue);
152
+
153
+ // Call from rejecter itself so msg.sender (rejecter) cannot receive the refund
154
+ vm.prank(address(rejecter));
155
+ vm.expectRevert(IncoUtils.RefundFailed.selector);
156
+ rejecter.callWithRefund{value: msgValue}();
157
+ }
158
+
159
+ function testRefundUnspentWithZeroMsgValue() public {
160
+ // Set up some initial balance
161
+ vm.deal(address(this), 1 ether);
162
+ uint256 balanceBefore = address(this).balance;
163
+
164
+ // Call with zero msg.value - should not revert and no refund needed
165
+ this.mockFunctionNoSpend{value: 0}();
166
+
167
+ // Balance should be unchanged
168
+ assertEq(address(this).balance, balanceBefore);
169
+ }
170
+
171
+ function testRefundUnspentWithWeiPrecision() public {
172
+ uint256 msgValue = 1000 wei;
173
+ uint256 spendAmount = 333 wei;
174
+
175
+ vm.deal(address(this), msgValue);
176
+ uint256 balanceBefore = address(this).balance;
177
+
178
+ this.mockFunctionPartialSpend{value: msgValue}(spendAmount);
179
+
180
+ // Check exact wei-level precision
181
+ assertEq(address(this).balance, balanceBefore - spendAmount);
182
+ }
183
+
184
+ function testRefundUnspentWhenContractReceivesEthDuringExecution() public {
185
+ uint256 msgValue = 1 ether;
186
+ uint256 incomingEth = 0.5 ether;
187
+
188
+ // Fund the sender contract
189
+ vm.deal(address(sender), incomingEth);
190
+ vm.deal(address(this), msgValue);
191
+ uint256 balanceBefore = address(this).balance;
192
+
193
+ // During execution, sender will send us 0.5 ETH
194
+ // This means balanceAfter > balanceBefore, so spent = 0
195
+ // Refund should be capped at msg.value (1 ETH)
196
+ this.mockFunctionReceivesEthDuringExecution{value: msgValue}(payable(address(sender)), incomingEth);
197
+
198
+ // We sent 1 ETH, received 0.5 ETH during execution, and got 1 ETH refunded
199
+ // Final: balanceBefore - msgValue + incomingEth + msgValue = balanceBefore + incomingEth
200
+ assertEq(address(this).balance, balanceBefore + incomingEth);
201
+ }
202
+
203
+ function testRefundUnspentWhenSpendingExactlyMsgValue() public {
204
+ // Edge case: spend exactly msg.value (boundary condition)
205
+ // This is similar to testRefundUnspentWhenFullSpend but uses mockFunctionPartialSpend
206
+ uint256 msgValue = 1 ether;
207
+
208
+ vm.deal(address(this), msgValue);
209
+ uint256 balanceBefore = address(this).balance;
210
+
211
+ // Spend exactly msg.value using partial spend function
212
+ this.mockFunctionPartialSpend{value: msgValue}(msgValue);
213
+
214
+ // No refund should occur - spent exactly what was sent
215
+ assertEq(address(this).balance, balanceBefore - msgValue);
216
+ }
217
+
218
+ function testRefundUnspentReentrancyProtection() public {
219
+ ReentrantAttacker attacker = new ReentrantAttacker(this);
220
+ vm.deal(address(attacker), 2 ether);
221
+
222
+ // When the attacker tries to re-enter, ReentrantCall() is thrown inside the receive()
223
+ // This causes the receive() to revert, which fails the refund .call(), surfacing RefundFailed()
224
+ vm.prank(address(attacker));
225
+ vm.expectRevert(IncoUtils.RefundFailed.selector);
226
+ attacker.attack{value: 1 ether}();
227
+ }
228
+
229
+ function testFuzzRefundUnspent(uint256 msgValue, uint256 spendAmount) public {
230
+ // Bound to reasonable values
231
+ msgValue = bound(msgValue, 0, 10 ether);
232
+ spendAmount = bound(spendAmount, 0, msgValue);
233
+
234
+ vm.deal(address(this), msgValue);
235
+ uint256 balanceBefore = address(this).balance;
236
+
237
+ this.mockFunctionPartialSpend{value: msgValue}(spendAmount);
238
+
239
+ assertEq(address(this).balance, balanceBefore - spendAmount);
240
+ }
241
+
242
+ }
@@ -7,12 +7,12 @@ pragma solidity ^0.8;
7
7
  // UPDATE the CHANGELOG on new versions
8
8
 
9
9
  string constant CONTRACT_NAME = "incoLightning";
10
- uint8 constant MAJOR_VERSION = 2;
10
+ uint8 constant MAJOR_VERSION = 3;
11
11
  uint8 constant MINOR_VERSION = 0;
12
12
  // whenever a new version is deployed, we need to pump this up
13
13
  // otherwise make test_upgrade will fail
14
14
  // consequently, when we do a patch release, we don't need to pump it as it's already pumped
15
15
  // when the previous release was done
16
- uint8 constant PATCH_VERSION = 1;
16
+ uint8 constant PATCH_VERSION = 2;
17
17
 
18
18
  string constant VERIFIER_NAME = "incoVerifier";
@@ -1,22 +0,0 @@
1
- // SPDX-License-Identifier: No License
2
- pragma solidity ^0.8;
3
-
4
- import {IEncryptedInput} from "./lightning-parts/interfaces/IEncryptedInput.sol";
5
- import {IEncryptedOperations} from "./lightning-parts/interfaces/IEncryptedOperations.sol";
6
- import {ITrivialEncryption} from "./lightning-parts/interfaces/ITrivialEncryption.sol";
7
- import {IBaseAccessControlList} from "./lightning-parts/AccessControl/interfaces/IBaseAccessControlList.sol";
8
- import {IHandleGeneration} from "./lightning-parts/primitives/interfaces/IHandleGeneration.sol";
9
- import {IVersion} from "./version/interfaces/IVersion.sol";
10
-
11
- interface IIncoLightning is
12
- IEncryptedInput,
13
- IEncryptedOperations,
14
- ITrivialEncryption,
15
- IBaseAccessControlList,
16
- IHandleGeneration,
17
- IVersion
18
- {
19
-
20
- function initialize(address owner) external;
21
-
22
- }