Skip to content

Commit 5e3bde6

Browse files
committed
test(rounding): implement integration rounding test
1 parent 0b6f6d8 commit 5e3bde6

File tree

1 file changed

+80
-15
lines changed

1 file changed

+80
-15
lines changed

src/test/integration/tests/Rounding.t.sol

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,66 +11,131 @@ contract Integration_Rounding is IntegrationCheckUtils {
1111
AVS badAVS;
1212
IStrategy strategy;
1313
IERC20Metadata token;
14+
User goodStaker;
1415

1516
OperatorSet mOpSet; // "manipOpSet" used for magnitude manipulation
1617
OperatorSet rOpSet; // redistributable opset used to exploit precision loss and trigger redistribution
1718

1819
function _init() internal override {
1920
_configAssetTypes(HOLDS_LST);
2021

21-
attacker = new User("Attacker");
22+
attacker = new User("Attacker"); // attacker can be both operator and staker
2223
badAVS = new AVS("BadAVS");
2324
strategy = lstStrats[0];
2425
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+
2632
// Register attacker as operator and create attacker-controller AVS/OpSets
2733
attacker.registerAsOperator(0);
2834
rollForward({blocks: ALLOCATION_CONFIGURATION_DELAY + 1});
2935
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
3238

3339
// Register for both opsets
3440
attacker.registerForOperatorSet(mOpSet);
3541
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 -)
3646

3747
_print("setup");
3848
}
3949

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);
4459
}
4560

4661
// 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
4865
attacker.modifyAllocations(AllocateParams({
4966
operatorSet: mOpSet,
5067
strategies: strategy.toArray(),
5168
newMagnitudes: WAD.toArrayU64()
5269
}));
70+
71+
// TODO: print "newMagnitudes"
5372

5473
_print("allocate");
5574

75+
// slash operator to low mag
5676
badAVS.slashOperator(SlashingParams({
5777
operator: address(attacker),
5878
operatorSetId: mOpSet.id,
5979
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!"
6282
}));
83+
84+
// TODO: print "wadsToSlash"
6385

6486
_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");
6598
}
6699

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");
70117
}
71118

72-
function _final() internal {
119+
function _final(uint64 wadToSlash) internal {
73120
// 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");
74139
}
75140

76141
function _print(string memory phaseName) internal {

0 commit comments

Comments
 (0)