@@ -17,6 +17,8 @@ contract Integration_Rounding is IntegrationCheckUtils {
1717 OperatorSet mOpSet; // "manipOpSet" used for magnitude manipulation
1818 OperatorSet rOpSet; // Redistributable opset used to exploit precision loss and trigger redistribution
1919
20+ uint numSlashes = 50 ;
21+
2022 function _init () internal override {
2123 _configAssetTypes (HOLDS_LST);
2224
@@ -28,7 +30,8 @@ contract Integration_Rounding is IntegrationCheckUtils {
2830 // Prepares to add non-attacker stake into the protocol. Can be any amount > 0.
2931 // Note that the honest stake does not need to be allocated anywhere, so long as it's in the same strategy.
3032 goodStaker = new User ("GoodStaker " );
31- deal (address (token), address (goodStaker), uint256 (1e18 ));
33+ deal (address (token), address (goodStaker), uint256 (1e18 ));
34+ goodStaker.depositIntoEigenlayer (strategy.toArray (), 1e18 .toArrayU256 ());
3235
3336 // Register attacker as operator and create attacker-controlled AVS/OpSets
3437 attacker.registerAsOperator (0 );
@@ -45,36 +48,33 @@ contract Integration_Rounding is IntegrationCheckUtils {
4548 }
4649
4750 // TODO: consider incremental manual fuzzing from 1 up to WAD - 1
48- function test_rounding_allMagsSlashed (uint16 slashes , uint64 initialWadToSlash , uint64 _initTokenBalance , uint24 r ) public rand (r) {
51+ function test_rounding_allMagsSlashed (
52+ uint64 initialWadToSlash ,
53+ uint64 _initTokenBalance
54+ ) public rand (0 ) {
4955 vm.pauseGasMetering ();
50- // Bound slashes to a reasonable range, with at least 1 slash.
51- // Note: Runs after 2500 hit OOG errors with default gas.
52- slashes = uint16 (bound (slashes, 1 , 5000 ));
53-
54- // We do two slashes, the sum of which slash 1 WAD (all operator magnitude) in total.
55- // Each slash requires at least 1 mag. As such, we need to bound wadToSlash to 1 <= wadToSlash <= WAD - 1.
56+ // Don't slash 100% as we will do multiple slashes
5657 initialWadToSlash = uint64 (bound (initialWadToSlash, 1 , WAD - 1 ));
57-
58- // Bound initTokenBalance to a reasonable range to avoid overflow, with at least 1 token.
59- // Using ~18.45 quintillion tokens max (should be enough for any realistic test).
60- initTokenBalance = uint64 (bound (_initTokenBalance, 1 , type (uint64 ).max));
58+ // Ensure attacker has at least one token
59+ initTokenBalance = _initTokenBalance > 0 ? _initTokenBalance : _initTokenBalance + 1 ;
6160 deal (address (token), address (attacker), initTokenBalance);
6261
63- _magnitudeManipulation (initialWadToSlash); // Manipulate operator magnitude for a given strategy.
64- _deposit (initialWadToSlash); // Setup operator with new opSet as well as honest stake in same strategy.
62+ // Use modifyAllocation+slashOperator to arbitrarily set operator max magnitude
63+ _magnitudeManipulation (initialWadToSlash);
64+ _deposit ();
6565
6666 // Perform slashes to gradually whittle down operator magnitude, as well as produce slash escrows.
67- for ( uint16 i = 0 ; i < slashes; i ++ ) {
68- // Upper bound is less than WAD to leave some mag for final slash
69- uint64 wadToSlash = uint64 (_randUint (1 , WAD - 1 ));
67+ // Since we're doing multiple slashes, never slash 100%.
68+ for ( uint16 i = 0 ; i < numSlashes; i ++ ) {
69+ uint64 wadToSlash = uint64 (cheats. randomUint (1 , WAD - 1 ));
7070 _slash (wadToSlash);
7171 }
7272
73- // Perform final slash to attempt to extract surplus value, where WAD represents 100% of operator magnitude .
73+ // Perform final 100% slash to extract any remaining tokens .
7474 _slash (WAD);
7575
7676 // Release all escrows to the redistributionRecipient (attacker).
77- _release (slashes );
77+ _release ();
7878
7979 // Check for any surplus value extracted by attacker. If found, we've proven the existence of the exploit.
8080 // Unchecked to avoid overflow reverting. Safe because token balances are bounded by uint64.
@@ -93,33 +93,30 @@ contract Integration_Rounding is IntegrationCheckUtils {
9393 }
9494 }
9595
96- function test_rounding_partialMagsSlashed (uint16 slashes , uint64 initialWadToSlash , uint64 _initTokenBalance , uint24 r ) public rand (r) {
96+ function test_rounding_partialMagsSlashed (
97+ uint64 initialWadToSlash ,
98+ uint64 _initTokenBalance
99+ ) public rand (0 ) {
97100 vm.pauseGasMetering ();
98- // Bound slashes to a reasonable range, with at least 1 slash.
99- // Note: Runs after 2500 hit OOG errors with default gas.
100- slashes = uint16 (bound (slashes, 0 , 500 ));
101-
102- // We do two slashes, the sum of which slash 1 WAD (all operator magnitude) in total.
103- // Each slash requires at least 1 mag. As such, we need to bound wadToSlash to 1 <= wadToSlash <= WAD - 1.
101+ // Don't slash 100% as we will do multiple slashes
104102 initialWadToSlash = uint64 (bound (initialWadToSlash, 1 , WAD - 1 ));
105-
106- // Bound initTokenBalance to a reasonable range to avoid overflow, with at least 1 token.
107- // Using ~18.45 quintillion tokens max (should be enough for any realistic test).
108- initTokenBalance = uint64 (bound (_initTokenBalance, 1 , type (uint64 ).max));
103+ // Ensure attacker has at least one token
104+ initTokenBalance = _initTokenBalance > 0 ? _initTokenBalance : _initTokenBalance + 1 ;
109105 deal (address (token), address (attacker), initTokenBalance);
110106
111- _magnitudeManipulation (initialWadToSlash); // Manipulate operator magnitude for a given strategy.
112- _deposit (initialWadToSlash); // Setup operator with new opSet as well as honest stake in same strategy.
107+ // Use modifyAllocation+slashOperator to arbitrarily set operator max magnitude
108+ _magnitudeManipulation (initialWadToSlash);
109+ _deposit ();
113110
114111 // Perform slashes to gradually whittle down operator magnitude, as well as produce slash escrows.
115- for ( uint16 i = 0 ; i < slashes; i ++ ) {
116- // Upper bound is less than WAD to leave some mag for final slash
117- uint64 wadToSlash = uint64 (_randUint (1 , WAD - 1 ));
112+ // Since we're doing multiple slashes, never slash 100%.
113+ for ( uint16 i = 0 ; i < numSlashes; i ++ ) {
114+ uint64 wadToSlash = uint64 (cheats. randomUint (1 , WAD - 1 ));
118115 _slash (wadToSlash);
119116 }
120117
121118 // Release all escrows to the redistributionRecipient (attacker).
122- _release (slashes );
119+ _release ();
123120
124121 // Withdraw all attacker deposits.
125122 (, uint256 [] memory depositShares ) = strategyManager.getDeposits (address (attacker));
@@ -182,20 +179,18 @@ contract Integration_Rounding is IntegrationCheckUtils {
182179 _print ("deallocate " );
183180 }
184181
185- function _deposit (uint64 wadToSlash ) internal {
182+ function _deposit () internal {
186183 // Allocate all remaining magnitude to redistributable opset.
184+ uint64 allocatableMagnitude = allocationManager.getAllocatableMagnitude (address (attacker), strategy);
187185 attacker.modifyAllocations (AllocateParams ({
188186 operatorSet: rOpSet,
189187 strategies: strategy.toArray (),
190- newMagnitudes: (WAD - wadToSlash ).toArrayU64 ()
188+ newMagnitudes: (allocatableMagnitude ).toArrayU64 ()
191189 }));
192190
193191 // Deposit all attacker assets into Eigenlayer.
194192 attacker.depositIntoEigenlayer (strategy.toArray (), token.balanceOf (address (attacker)).toArrayU256 ());
195193
196- // Deposit all honest stake into Eigenlayer.
197- goodStaker.depositIntoEigenlayer (strategy.toArray (), token.balanceOf (address (goodStaker)).toArrayU256 ());
198-
199194 _print ("deposit " );
200195 }
201196
@@ -212,19 +207,19 @@ contract Integration_Rounding is IntegrationCheckUtils {
212207 _print ("slash " );
213208 }
214209
215- function _release (uint64 slashes ) internal {
210+ function _release () internal {
216211 // Roll forward past the escrow delay.
217212 rollForward ({blocks: slashEscrowFactory.getGlobalEscrowDelay () + 1 });
218213
219214 // Release funds.
220- for (uint32 i = 1 ; i <= slashes ; i++ ) {
215+ for (uint32 i = 1 ; i <= numSlashes ; i++ ) {
221216 vm.prank (address (attacker));
222217 slashEscrowFactory.releaseSlashEscrow (rOpSet, i);
223218 }
224219
225220 // Release final escrow.
226221 vm.prank (address (attacker));
227- slashEscrowFactory.releaseSlashEscrow (rOpSet, uint256 (slashes ) + 1 );
222+ slashEscrowFactory.releaseSlashEscrow (rOpSet, uint256 (numSlashes ) + 1 );
228223
229224 _print ("release " );
230225 }
@@ -274,4 +269,4 @@ contract Integration_Rounding is IntegrationCheckUtils {
274269
275270 console.log ("\n ===\n " .cyan ());
276271 }
277- }
272+ }
0 commit comments