Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d89440a

Browse files
committed
fix: consider delegation when deciding if an indexer is disputable
Signed-off-by: Tomás Migone <tomas@edgeandnode.com>
1 parent 97dceb1 commit d89440a

File tree

3 files changed

+57
-22
lines changed

3 files changed

+57
-22
lines changed

‎packages/subgraph-service/contracts/DisputeManager.sol

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,7 @@ contract DisputeManager is
344344

345345
/// @inheritdoc IDisputeManager
346346
function getStakeSnapshot(address indexer) external view override returns (uint256) {
347-
IHorizonStaking.Provision memory provision = _graphStaking().getProvision(
348-
indexer,
349-
address(_getSubgraphService())
350-
);
351-
return _getStakeSnapshot(indexer, provision.tokens);
347+
return _getStakeSnapshot(indexer);
352348
}
353349

354350
/// @inheritdoc IDisputeManager
@@ -398,13 +394,6 @@ contract DisputeManager is
398394
// Get the indexer that signed the attestation
399395
address indexer = getAttestationIndexer(_attestation);
400396

401-
// The indexer is disputable
402-
IHorizonStaking.Provision memory provision = _graphStaking().getProvision(
403-
indexer,
404-
address(_getSubgraphService())
405-
);
406-
require(provision.tokens != 0, DisputeManagerZeroTokens());
407-
408397
// Create a disputeId
409398
bytes32 disputeId = keccak256(
410399
abi.encodePacked(
@@ -419,8 +408,11 @@ contract DisputeManager is
419408
// Only one dispute at a time
420409
require(!isDisputeCreated(disputeId), DisputeManagerDisputeAlreadyCreated(disputeId));
421410

411+
// The indexer is disputable
412+
uint256 stakeSnapshot = _getStakeSnapshot(indexer);
413+
require(stakeSnapshot != 0, DisputeManagerZeroTokens());
414+
422415
// Store dispute
423-
uint256 stakeSnapshot = _getStakeSnapshot(indexer, provision.tokens);
424416
uint256 cancellableAt = block.timestamp + disputePeriod;
425417
disputes[disputeId] = Dispute(
426418
indexer,
@@ -477,11 +469,10 @@ contract DisputeManager is
477469
require(indexer != address(0), DisputeManagerIndexerNotFound(_allocationId));
478470

479471
// The indexer must be disputable
480-
IHorizonStaking.Provision memory provision =_graphStaking().getProvision(indexer, address(subgraphService_));
481-
require(provision.tokens != 0, DisputeManagerZeroTokens());
472+
uint256 stakeSnapshot =_getStakeSnapshot(indexer);
473+
require(stakeSnapshot != 0, DisputeManagerZeroTokens());
482474

483475
// Store dispute
484-
uint256 stakeSnapshot = _getStakeSnapshot(indexer, provision.tokens);
485476
uint256 cancellableAt = block.timestamp + disputePeriod;
486477
disputes[disputeId] = Dispute(
487478
alloc.indexer,
@@ -691,18 +682,19 @@ contract DisputeManager is
691682
* @dev A few considerations:
692683
* - We include both indexer and delegators stake.
693684
* - Thawing stake is not excluded from the snapshot.
694-
* - Delegators stake is capped at the delegation ratio to prevent delegators from inflating the snapshot
695-
* to increase the indexer slash amount.
696685
*
697686
* Note that the snapshot can be inflated by delegators front-running the dispute creation with a delegation
698687
* to the indexer. Given the snapshot is a cap, the dispute outcome is uncertain and considering the cost of capital
699688
* and slashing risk, this is not a concern.
700689
* @param _indexer Indexer address
701-
* @param _indexerStake Indexer's stake
702690
* @return Total stake snapshot
703691
*/
704-
function _getStakeSnapshot(address _indexer, uint256 _indexerStake) private view returns (uint256) {
705-
uint256 delegatorsStake = _graphStaking().getDelegationPool(_indexer, address(_getSubgraphService())).tokens;
706-
return _indexerStake + delegatorsStake;
692+
function _getStakeSnapshot(address _indexer) private view returns (uint256) {
693+
address subgraphService = address(_getSubgraphService());
694+
695+
IHorizonStaking.Provision memory provision = _graphStaking().getProvision(_indexer, subgraphService);
696+
uint256 delegatorsStake = _graphStaking().getDelegationPool(_indexer, subgraphService).tokens;
697+
698+
return provision.tokens + delegatorsStake;
707699
}
708700
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,24 @@ contract DisputeManagerIndexingCreateDisputeTest is DisputeManagerTest {
157157
vm.expectRevert(abi.encodeWithSelector(IDisputeManager.DisputeManagerZeroTokens.selector));
158158
disputeManager.createIndexingDispute(allocationID, bytes32("POI1"), block.number);
159159
}
160+
161+
function test_Indexing_Create_DontRevertIf_IndexerIsBelowStake_WithDelegation(uint256 tokens, uint256 delegationTokens) public useIndexer useAllocation(tokens) {
162+
// Close allocation
163+
bytes memory data = abi.encode(allocationID);
164+
_stopService(users.indexer, data);
165+
// Thaw, deprovision and unstake
166+
address subgraphDataServiceAddress = address(subgraphService);
167+
_thawDeprovisionAndUnstake(users.indexer, subgraphDataServiceAddress, tokens);
168+
169+
delegationTokens = bound(delegationTokens, 1 ether, 100_000_000 ether);
170+
171+
resetPrank(users.delegator);
172+
token.approve(address(staking), delegationTokens);
173+
staking.delegate(users.indexer, address(subgraphService), delegationTokens, 0);
174+
175+
// create dispute
176+
resetPrank(users.fisherman);
177+
token.approve(address(disputeManager), tokens);
178+
_createIndexingDispute(allocationID, bytes32("POI1"), block.number);
179+
}
160180
}

‎packages/subgraph-service/test/unit/disputeManager/disputes/query/create.t.sol

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,27 @@ contract DisputeManagerQueryCreateDisputeTest is DisputeManagerTest {
155155
vm.expectRevert(abi.encodeWithSelector(IDisputeManager.DisputeManagerZeroTokens.selector));
156156
disputeManager.createQueryDispute(attestationData);
157157
}
158+
159+
function test_Query_Create_DontRevertIf_IndexerIsBelowStake_WithDelegation(uint256 tokens, uint256 delegationTokens) public useIndexer useAllocation(tokens) {
160+
// Close allocation
161+
bytes memory data = abi.encode(allocationID);
162+
_stopService(users.indexer, data);
163+
// Thaw, deprovision and unstake
164+
address subgraphDataServiceAddress = address(subgraphService);
165+
_thawDeprovisionAndUnstake(users.indexer, subgraphDataServiceAddress, tokens);
166+
167+
delegationTokens = bound(delegationTokens, 1 ether, 100_000_000 ether);
168+
169+
resetPrank(users.delegator);
170+
token.approve(address(staking), delegationTokens);
171+
staking.delegate(users.indexer, address(subgraphService), delegationTokens, 0);
172+
173+
// create dispute
174+
resetPrank(users.fisherman);
175+
token.approve(address(disputeManager), tokens);
176+
177+
Attestation.Receipt memory receipt = _createAttestationReceipt(requestCID, responseCID, subgraphDeploymentId);
178+
bytes memory attestationData = _createAtestationData(receipt, allocationIDPrivateKey);
179+
_createQueryDispute(attestationData);
180+
}
158181
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /