@@ -11,66 +11,131 @@ contract Integration_Rounding is IntegrationCheckUtils {
11
11
AVS badAVS;
12
12
IStrategy strategy;
13
13
IERC20Metadata token;
14
+ User goodStaker;
14
15
15
16
OperatorSet mOpSet; // "manipOpSet" used for magnitude manipulation
16
17
OperatorSet rOpSet; // redistributable opset used to exploit precision loss and trigger redistribution
17
18
18
19
function _init () internal override {
19
20
_configAssetTypes (HOLDS_LST);
20
21
21
- attacker = new User ("Attacker " );
22
+ attacker = new User ("Attacker " ); // attacker can be both operator and staker
22
23
badAVS = new AVS ("BadAVS " );
23
24
strategy = lstStrats[0 ];
24
25
token = IERC20Metadata (address (strategy.underlyingToken ()));
25
-
26
+ deal (address (token), address (attacker), uint256 (1000000000000000000 )); // TODO: make the balance a fuzzing param
27
+
28
+ // good staker and operator setup
29
+ goodStaker = new User ("goodStaker " );
30
+ deal (address (token), address (goodStaker), uint256 (1000000000000000000 )); // TODO: make the balance a fuzzing param
31
+
26
32
// Register attacker as operator and create attacker-controller AVS/OpSets
27
33
attacker.registerAsOperator (0 );
28
34
rollForward ({blocks: ALLOCATION_CONFIGURATION_DELAY + 1 });
29
35
badAVS.updateAVSMetadataURI ("https://example.com " );
30
- mOpSet = badAVS.createOperatorSet (strategy.toArray ());
31
- rOpSet = badAVS.createRedistributingOperatorSet (strategy.toArray (), address (attacker));
36
+ mOpSet = badAVS.createOperatorSet (strategy.toArray ()); // setup low mag operator
37
+ rOpSet = badAVS.createRedistributingOperatorSet (strategy.toArray (), address (attacker)); // execute exploit
32
38
33
39
// Register for both opsets
34
40
attacker.registerForOperatorSet (mOpSet);
35
41
attacker.registerForOperatorSet (rOpSet);
42
+
43
+ // TODO: considerations
44
+ // - assets of other users within the opSet
45
+ // - calculate total assets entering and leaving the protocol, estimate precision loss (+ or -)
36
46
37
47
_print ("setup " );
38
48
}
39
49
40
- function test_rounding () public rand (0 ) {
41
- _magnitudeManipulation ();
42
- _setupFinal ();
43
- _final ();
50
+ // TODO: parameterize deposits
51
+ // TODO: consider manual fuzzing from 1 up to WAD - 1
52
+ function test_rounding (uint64 wadToSlash ) public rand (0 ) {
53
+ // bound wadToSlash to 1 < wadToSlash < WAD - 1
54
+ wadToSlash = uint64 (bound (wadToSlash, 1 , WAD - 1 ));
55
+
56
+ _magnitudeManipulation (wadToSlash); // get operator to low mag
57
+ _setupFinal (wadToSlash); //
58
+ _final (wadToSlash);
44
59
}
45
60
46
61
// TODO - another way to mess with rounding/precision loss is to manipulate DSF
47
- function _magnitudeManipulation () internal {
62
+ function _magnitudeManipulation (uint64 wadToSlash ) internal {
63
+
64
+ // allocate magnitude to operator set
48
65
attacker.modifyAllocations (AllocateParams ({
49
66
operatorSet: mOpSet,
50
67
strategies: strategy.toArray (),
51
68
newMagnitudes: WAD.toArrayU64 ()
52
69
}));
70
+
71
+ // TODO: print "newMagnitudes"
53
72
54
73
_print ("allocate " );
55
74
75
+ // slash operator to low mag
56
76
badAVS.slashOperator (SlashingParams ({
57
77
operator: address (attacker),
58
78
operatorSetId: mOpSet.id,
59
79
strategies: strategy.toArray (),
60
- wadsToSlash: uint (WAD -1 ).toArrayU256 (),
61
- description: ""
80
+ wadsToSlash: uint (wadToSlash ).toArrayU256 (), // TODO: Make WAD-1 a fuzzing param
81
+ description: "manipulation! "
62
82
}));
83
+
84
+ // TODO: print "wadsToSlash"
63
85
64
86
_print ("slash " );
87
+
88
+ // deallocate magnitude from operator set
89
+ attacker.modifyAllocations (AllocateParams ({
90
+ operatorSet: mOpSet,
91
+ strategies: strategy.toArray (),
92
+ newMagnitudes: 0 .toArrayU64 ()
93
+ }));
94
+
95
+ rollForward ({blocks: DEALLOCATION_DELAY + 1 });
96
+
97
+ _print ("deallocate " );
65
98
}
66
99
67
- function _setupFinal () internal {
68
- // create redistributable opset
69
- // deposit assets
100
+ function _setupFinal (uint64 wadToSlash ) internal {
101
+ // allocate to redistributable opset
102
+ attacker.modifyAllocations (AllocateParams ({
103
+ operatorSet: rOpSet,
104
+ strategies: strategy.toArray (),
105
+ newMagnitudes: (WAD - wadToSlash).toArrayU64 ()
106
+ }));
107
+
108
+ // deposit assets and delegate to attacker
109
+ attacker.depositIntoEigenlayer (strategy.toArray (), token.balanceOf (address (attacker)).toArrayU256 ());
110
+
111
+
112
+ goodStaker.depositIntoEigenlayer (strategy.toArray (), token.balanceOf (address (goodStaker)).toArrayU256 ());
113
+
114
+ // TODO: print "initTokenBalances"
115
+
116
+ _print ("deposit " );
70
117
}
71
118
72
- function _final () internal {
119
+ function _final (uint64 wadToSlash ) internal {
73
120
// perform final slash on redistributable opset and check for profit
121
+ badAVS.slashOperator (SlashingParams ({
122
+ operator: address (attacker),
123
+ operatorSetId: rOpSet.id,
124
+ strategies: strategy.toArray (),
125
+ wadsToSlash: uint (WAD - wadToSlash).toArrayU256 (),
126
+ description: "final slash "
127
+ }));
128
+
129
+ _print ("slash " );
130
+
131
+ // roll forward past the escrow delay
132
+ rollForward ({blocks: slashEscrowFactory.getGlobalEscrowDelay () + 1 });
133
+
134
+ // release funds
135
+ vm.prank (address (attacker));
136
+ slashEscrowFactory.releaseSlashEscrow (rOpSet, 1 ); // 1 is used as it's the first slashId
137
+
138
+ _print ("release " );
74
139
}
75
140
76
141
function _print (string memory phaseName ) internal {
0 commit comments