Skip to content

Commit 28d810e

Browse files
committed
Merge branch 'horizon' into tmigone/horizon-interfaces
2 parents 8b7f14c + 77007c0 commit 28d810e

File tree

12 files changed

+114
-56
lines changed

12 files changed

+114
-56
lines changed
122 KB
Binary file not shown.

packages/horizon/contracts/staking/HorizonStakingExtension.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ contract HorizonStakingExtension is HorizonStakingBase, IHorizonStakingExtension
358358
// Process non-zero-allocation rewards tracking
359359
if (alloc.tokens > 0) {
360360
// Distribute rewards if proof of indexing was presented by the indexer or operator
361-
if (isIndexerOrOperator && _poi != 0) {
361+
if (isIndexerOrOperator && _poi != 0 && epochs > 0) {
362362
_distributeRewards(_allocationID, alloc.indexer);
363363
} else {
364364
_updateRewards(alloc.subgraphDeploymentID);

packages/interfaces/contracts/subgraph-service/IDisputeManager.sol

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ interface IDisputeManager {
123123
* @param tokens The amount of tokens deposited by the fisherman
124124
* @param allocationId The allocation id
125125
* @param poi The POI
126+
* @param blockNumber The block number for which the POI was calculated
126127
* @param stakeSnapshot The stake snapshot of the indexer at the time of the dispute
127128
* @param cancellableAt The timestamp when the dispute can be cancelled
128129
*/
@@ -133,6 +134,7 @@ interface IDisputeManager {
133134
uint256 tokens,
134135
address allocationId,
135136
bytes32 poi,
137+
uint256 blockNumber,
136138
uint256 stakeSnapshot,
137139
uint256 cancellableAt
138140
);
@@ -458,9 +460,10 @@ interface IDisputeManager {
458460
*
459461
* @param allocationId The allocation to dispute
460462
* @param poi The Proof of Indexing (POI) being disputed
463+
* @param blockNumber The block number for which the POI was calculated
461464
* @return The dispute id
462465
*/
463-
function createIndexingDispute(address allocationId, bytes32 poi) external returns (bytes32);
466+
function createIndexingDispute(address allocationId, bytes32 poi, uint256 blockNumber) external returns (bytes32);
464467

465468
/**
466469
* @notice Creates and auto-accepts a legacy dispute.

packages/subgraph-service/contracts/DisputeManager.sol

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,16 @@ contract DisputeManager is
121121
}
122122

123123
/// @inheritdoc IDisputeManager
124-
function createIndexingDispute(address allocationId, bytes32 poi) external override returns (bytes32) {
124+
function createIndexingDispute(
125+
address allocationId,
126+
bytes32 poi,
127+
uint256 blockNumber
128+
) external override returns (bytes32) {
125129
// Get funds from fisherman
126130
_graphToken().pullTokens(msg.sender, disputeDeposit);
127131

128132
// Create a dispute
129-
return _createIndexingDisputeWithAllocation(msg.sender, disputeDeposit, allocationId, poi);
133+
return _createIndexingDisputeWithAllocation(msg.sender, disputeDeposit, allocationId, poi, blockNumber);
130134
}
131135

132136
/// @inheritdoc IDisputeManager
@@ -341,11 +345,7 @@ contract DisputeManager is
341345

342346
/// @inheritdoc IDisputeManager
343347
function getStakeSnapshot(address indexer) external view override returns (uint256) {
344-
IHorizonStaking.Provision memory provision = _graphStaking().getProvision(
345-
indexer,
346-
address(_getSubgraphService())
347-
);
348-
return _getStakeSnapshot(indexer, provision.tokens);
348+
return _getStakeSnapshot(indexer);
349349
}
350350

351351
/// @inheritdoc IDisputeManager
@@ -395,13 +395,6 @@ contract DisputeManager is
395395
// Get the indexer that signed the attestation
396396
address indexer = getAttestationIndexer(_attestation);
397397

398-
// The indexer is disputable
399-
IHorizonStaking.Provision memory provision = _graphStaking().getProvision(
400-
indexer,
401-
address(_getSubgraphService())
402-
);
403-
require(provision.tokens != 0, DisputeManagerZeroTokens());
404-
405398
// Create a disputeId
406399
bytes32 disputeId = keccak256(
407400
abi.encodePacked(
@@ -416,8 +409,11 @@ contract DisputeManager is
416409
// Only one dispute at a time
417410
require(!isDisputeCreated(disputeId), DisputeManagerDisputeAlreadyCreated(disputeId));
418411

412+
// The indexer is disputable
413+
uint256 stakeSnapshot = _getStakeSnapshot(indexer);
414+
require(stakeSnapshot != 0, DisputeManagerZeroTokens());
415+
419416
// Store dispute
420-
uint256 stakeSnapshot = _getStakeSnapshot(indexer, provision.tokens);
421417
uint256 cancellableAt = block.timestamp + disputePeriod;
422418
disputes[disputeId] = Dispute(
423419
indexer,
@@ -451,16 +447,18 @@ contract DisputeManager is
451447
* @param _deposit Amount of tokens staked as deposit
452448
* @param _allocationId Allocation disputed
453449
* @param _poi The POI being disputed
450+
* @param _blockNumber The block number for which the POI was calculated
454451
* @return The dispute id
455452
*/
456453
function _createIndexingDisputeWithAllocation(
457454
address _fisherman,
458455
uint256 _deposit,
459456
address _allocationId,
460-
bytes32 _poi
457+
bytes32 _poi,
458+
uint256 _blockNumber
461459
) private returns (bytes32) {
462460
// Create a disputeId
463-
bytes32 disputeId = keccak256(abi.encodePacked(_allocationId, _poi));
461+
bytes32 disputeId = keccak256(abi.encodePacked(_allocationId, _poi, _blockNumber));
464462

465463
// Only one dispute for an allocationId at a time
466464
require(!isDisputeCreated(disputeId), DisputeManagerDisputeAlreadyCreated(disputeId));
@@ -472,11 +470,10 @@ contract DisputeManager is
472470
require(indexer != address(0), DisputeManagerIndexerNotFound(_allocationId));
473471

474472
// The indexer must be disputable
475-
IHorizonStaking.Provision memory provision = _graphStaking().getProvision(indexer, address(subgraphService_));
476-
require(provision.tokens != 0, DisputeManagerZeroTokens());
473+
uint256 stakeSnapshot = _getStakeSnapshot(indexer);
474+
require(stakeSnapshot != 0, DisputeManagerZeroTokens());
477475

478476
// Store dispute
479-
uint256 stakeSnapshot = _getStakeSnapshot(indexer, provision.tokens);
480477
uint256 cancellableAt = block.timestamp + disputePeriod;
481478
disputes[disputeId] = Dispute(
482479
alloc.indexer,
@@ -497,6 +494,7 @@ contract DisputeManager is
497494
_deposit,
498495
_allocationId,
499496
_poi,
497+
_blockNumber,
500498
stakeSnapshot,
501499
cancellableAt
502500
);
@@ -685,18 +683,19 @@ contract DisputeManager is
685683
* @dev A few considerations:
686684
* - We include both indexer and delegators stake.
687685
* - Thawing stake is not excluded from the snapshot.
688-
* - Delegators stake is capped at the delegation ratio to prevent delegators from inflating the snapshot
689-
* to increase the indexer slash amount.
690686
*
691687
* Note that the snapshot can be inflated by delegators front-running the dispute creation with a delegation
692688
* to the indexer. Given the snapshot is a cap, the dispute outcome is uncertain and considering the cost of capital
693689
* and slashing risk, this is not a concern.
694690
* @param _indexer Indexer address
695-
* @param _indexerStake Indexer's stake
696691
* @return Total stake snapshot
697692
*/
698-
function _getStakeSnapshot(address _indexer, uint256 _indexerStake) private view returns (uint256) {
699-
uint256 delegatorsStake = _graphStaking().getDelegationPool(_indexer, address(_getSubgraphService())).tokens;
700-
return _indexerStake + delegatorsStake;
693+
function _getStakeSnapshot(address _indexer) private view returns (uint256) {
694+
address subgraphService = address(_getSubgraphService());
695+
696+
IHorizonStaking.Provision memory provision = _graphStaking().getProvision(_indexer, subgraphService);
697+
uint256 delegatorsStake = _graphStaking().getDelegationPool(_indexer, subgraphService).tokens;
698+
699+
return provision.tokens + delegatorsStake;
701700
}
702701
}

packages/subgraph-service/test/unit/disputeManager/DisputeManager.t.sol

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,9 @@ contract DisputeManagerTest is SubgraphServiceSharedTest {
7070
assertEq(address(disputeManager.subgraphService()), _subgraphService, "Subgraph service should be set.");
7171
}
7272

73-
function _createIndexingDispute(address _allocationId, bytes32 _poi) internal returns (bytes32) {
73+
function _createIndexingDispute(address _allocationId, bytes32 _poi, uint256 _blockNumber) internal returns (bytes32) {
7474
(, address fisherman, ) = vm.readCallers();
75-
bytes32 expectedDisputeId = keccak256(abi.encodePacked(_allocationId, _poi));
75+
bytes32 expectedDisputeId = keccak256(abi.encodePacked(_allocationId, _poi, _blockNumber));
7676
uint256 disputeDeposit = disputeManager.disputeDeposit();
7777
uint256 beforeFishermanBalance = token.balanceOf(fisherman);
7878
IAllocation.State memory alloc = subgraphService.getAllocation(_allocationId);
@@ -89,13 +89,14 @@ contract DisputeManagerTest is SubgraphServiceSharedTest {
8989
fisherman,
9090
disputeDeposit,
9191
_allocationId,
92-
_poi,
92+
_poi,
93+
_blockNumber,
9394
stakeSnapshot,
9495
cancellableAt
9596
);
9697

9798
// Create the indexing dispute
98-
bytes32 _disputeId = disputeManager.createIndexingDispute(_allocationId, _poi);
99+
bytes32 _disputeId = disputeManager.createIndexingDispute(_allocationId, _poi, _blockNumber);
99100

100101
// Check that the dispute was created and that it has the correct ID
101102
assertTrue(disputeManager.isDisputeCreated(_disputeId), "Dispute should be created.");

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ contract DisputeManagerDisputeTest is DisputeManagerTest {
2929

3030
function test_Dispute_Accept_RevertIf_SlashZeroTokens(uint256 tokens) public useIndexer useAllocation(tokens) {
3131
resetPrank(users.fisherman);
32-
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI101"));
32+
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI101"), block.number);
3333

3434
// attempt to accept dispute with 0 tokens slashed
3535
resetPrank(users.arbitrator);

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ contract DisputeManagerIndexingAcceptDisputeTest is DisputeManagerTest {
1818
tokensSlash = bound(tokensSlash, 1, uint256(maxSlashingPercentage).mulPPM(tokens));
1919

2020
resetPrank(users.fisherman);
21-
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"));
21+
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"), block.number);
2222

2323
resetPrank(users.arbitrator);
2424
_acceptDispute(disputeID, tokensSlash);
@@ -31,7 +31,7 @@ contract DisputeManagerIndexingAcceptDisputeTest is DisputeManagerTest {
3131
tokensSlash = bound(tokensSlash, 1, uint256(maxSlashingPercentage).mulPPM(tokens));
3232

3333
resetPrank(users.fisherman);
34-
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"));
34+
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"), block.number);
3535

3636
resetPrank(users.arbitrator);
3737
// clear subgraph service address from storage
@@ -48,7 +48,7 @@ contract DisputeManagerIndexingAcceptDisputeTest is DisputeManagerTest {
4848
tokensSlash = bound(tokensSlash, 1, uint256(maxSlashingPercentage).mulPPM(tokens));
4949

5050
resetPrank(users.fisherman);
51-
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"));
51+
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"), block.number);
5252

5353
resetPrank(users.arbitrator);
5454
_acceptDispute(disputeID, tokensSlash);
@@ -61,7 +61,7 @@ contract DisputeManagerIndexingAcceptDisputeTest is DisputeManagerTest {
6161
tokensSlash = bound(tokensSlash, 1, uint256(maxSlashingPercentage).mulPPM(tokens));
6262

6363
resetPrank(users.fisherman);
64-
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"));
64+
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"), block.number);
6565

6666
// attempt to accept dispute as fisherman
6767
resetPrank(users.fisherman);
@@ -75,7 +75,7 @@ contract DisputeManagerIndexingAcceptDisputeTest is DisputeManagerTest {
7575
) public useIndexer useAllocation(tokens) {
7676
resetPrank(users.fisherman);
7777
tokensSlash = bound(tokensSlash, uint256(maxSlashingPercentage).mulPPM(tokens) + 1, type(uint256).max);
78-
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI101"));
78+
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI101"), block.number);
7979

8080
// max slashing percentage is 50%
8181
resetPrank(users.arbitrator);

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ contract DisputeManagerIndexingCancelDisputeTest is DisputeManagerTest {
1313

1414
function test_Indexing_Cancel_Dispute(uint256 tokens) public useIndexer useAllocation(tokens) {
1515
resetPrank(users.fisherman);
16-
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"));
16+
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"), block.number);
1717

1818
// skip to end of dispute period
1919
uint256 disputePeriod = disputeManager.disputePeriod();
@@ -26,7 +26,7 @@ contract DisputeManagerIndexingCancelDisputeTest is DisputeManagerTest {
2626
uint256 tokens
2727
) public useIndexer useAllocation(tokens) {
2828
resetPrank(users.fisherman);
29-
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"));
29+
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"), block.number);
3030

3131
resetPrank(users.arbitrator);
3232
vm.expectRevert(abi.encodeWithSelector(IDisputeManager.DisputeManagerNotFisherman.selector));
@@ -37,15 +37,15 @@ contract DisputeManagerIndexingCancelDisputeTest is DisputeManagerTest {
3737
uint256 tokens
3838
) public useIndexer useAllocation(tokens) {
3939
resetPrank(users.fisherman);
40-
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"));
40+
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"), block.number);
4141

4242
vm.expectRevert(abi.encodeWithSelector(IDisputeManager.DisputeManagerDisputePeriodNotFinished.selector));
4343
disputeManager.cancelDispute(disputeID);
4444
}
4545

4646
function test_Indexing_Cancel_After_DisputePeriodIncreased(uint256 tokens) public useIndexer useAllocation(tokens) {
4747
resetPrank(users.fisherman);
48-
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"));
48+
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"), block.number);
4949

5050
// change the dispute period to a higher value
5151
uint256 oldDisputePeriod = disputeManager.disputePeriod();
@@ -62,7 +62,7 @@ contract DisputeManagerIndexingCancelDisputeTest is DisputeManagerTest {
6262

6363
function test_Indexing_Cancel_After_DisputePeriodDecreased(uint256 tokens) public useIndexer useAllocation(tokens) {
6464
resetPrank(users.fisherman);
65-
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"));
65+
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"), block.number);
6666

6767
// change the dispute period to a lower value
6868
uint256 oldDisputePeriod = disputeManager.disputePeriod();

0 commit comments

Comments
 (0)