Skip to content

Commit 06b3441

Browse files
committed
fix: ensure delegations are always slashable (OZ H-02)
Signed-off-by: Tomás Migone <[email protected]>
1 parent 3f01046 commit 06b3441

File tree

3 files changed

+34
-5
lines changed

3 files changed

+34
-5
lines changed

packages/subgraph-service/contracts/DisputeManager.sol

+6-5
Original file line numberDiff line numberDiff line change
@@ -677,15 +677,16 @@ contract DisputeManager is
677677
* - Thawing stake is not excluded from the snapshot.
678678
* - Delegators stake is capped at the delegation ratio to prevent delegators from inflating the snapshot
679679
* to increase the indexer slash amount.
680+
*
681+
* Note that the snapshot can be inflated by delegators front-running the dispute creation with a delegation
682+
* to the indexer. Given the snapshot is a cap, the dispute outcome is uncertain and considering the cost of capital
683+
* and slashing risk, this is not a concern.
680684
* @param _indexer Indexer address
681685
* @param _indexerStake Indexer's stake
682686
* @return Total stake snapshot
683687
*/
684688
function _getStakeSnapshot(address _indexer, uint256 _indexerStake) private view returns (uint256) {
685-
ISubgraphService subgraphService_ = _getSubgraphService();
686-
uint256 delegatorsStake = _graphStaking().getDelegationPool(_indexer, address(subgraphService_)).tokens;
687-
uint256 delegatorsStakeMax = _indexerStake * uint256(subgraphService_.getDelegationRatio());
688-
uint256 stakeSnapshot = _indexerStake + MathUtils.min(delegatorsStake, delegatorsStakeMax);
689-
return stakeSnapshot;
689+
uint256 delegatorsStake = _graphStaking().getDelegationPool(_indexer, address(_getSubgraphService())).tokens;
690+
return _indexerStake + delegatorsStake;
690691
}
691692
}

packages/subgraph-service/test/disputeManager/disputes/indexing/create.t.sol

+24
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,30 @@ contract DisputeManagerIndexingCreateDisputeTest is DisputeManagerTest {
1616
_createIndexingDispute(allocationID, bytes32("POI1"));
1717
}
1818

19+
function test_Indexing_Create_Dispute_WithDelegation(uint256 tokens, uint256 delegationTokens) public useIndexer {
20+
vm.assume(tokens >= minimumProvisionTokens);
21+
vm.assume(tokens < 100_000_000 ether); // set a low cap to test overdelegation
22+
_createProvision(users.indexer, tokens, fishermanRewardPercentage, disputePeriod);
23+
_register(users.indexer, abi.encode("url", "geoHash", address(0)));
24+
bytes memory data = _createSubgraphAllocationData(
25+
users.indexer,
26+
subgraphDeployment,
27+
allocationIDPrivateKey,
28+
tokens
29+
);
30+
_startService(users.indexer, data);
31+
32+
uint256 delegationRatio = subgraphService.getDelegationRatio();
33+
delegationTokens = bound(delegationTokens, 1e18, tokens * delegationRatio * 2); // make sure we have overdelegation
34+
35+
resetPrank(users.delegator);
36+
token.approve(address(staking), delegationTokens);
37+
staking.delegate(users.indexer, address(subgraphService), delegationTokens, 0);
38+
39+
resetPrank(users.fisherman);
40+
_createIndexingDispute(allocationID, bytes32("POI1"));
41+
}
42+
1943
function test_Indexing_Create_Dispute_RevertWhen_SubgraphServiceNotSet(
2044
uint256 tokens
2145
) public useIndexer useAllocation(tokens) {

packages/subgraph-service/test/shared/SubgraphServiceShared.t.sol

+4
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ abstract contract SubgraphServiceSharedTest is HorizonStakingSharedTest {
187187
staking.delegate(users.indexer, address(subgraphService), tokens, 0);
188188
}
189189

190+
function _calculateStakeSnapshot(uint256 _tokens, uint256 _tokensDelegated) internal view returns (uint256) {
191+
return _tokens + _tokensDelegated;
192+
}
193+
190194
/*
191195
* PRIVATE FUNCTIONS
192196
*/

0 commit comments

Comments
 (0)