Skip to content

fix: ensure dispute cancellation is not affected by dispute period change (OZ L-19) #1151

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: horizon-oz2/l13-partial-rav
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions packages/subgraph-service/contracts/DisputeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ contract DisputeManager is
DisputeType.LegacyDispute,
IDisputeManager.DisputeStatus.Accepted,
block.timestamp,
block.timestamp + disputePeriod,
0
);

Expand Down Expand Up @@ -285,7 +286,7 @@ contract DisputeManager is
Dispute storage dispute = disputes[disputeId];

// Check if dispute period has finished
require(dispute.createdAt + disputePeriod < block.timestamp, DisputeManagerDisputePeriodNotFinished());
require(dispute.cancellableAt <= block.timestamp, DisputeManagerDisputePeriodNotFinished());
_cancelDispute(disputeId, dispute);

if (_isDisputeInConflict(dispute)) {
Expand Down Expand Up @@ -417,6 +418,7 @@ contract DisputeManager is

// Store dispute
uint256 stakeSnapshot = _getStakeSnapshot(indexer, provision.tokens);
uint256 cancellableAt = block.timestamp + disputePeriod;
disputes[disputeId] = Dispute(
indexer,
_fisherman,
Expand All @@ -425,6 +427,7 @@ contract DisputeManager is
DisputeType.QueryDispute,
IDisputeManager.DisputeStatus.Pending,
block.timestamp,
cancellableAt,
stakeSnapshot
);

Expand All @@ -435,6 +438,7 @@ contract DisputeManager is
_deposit,
_attestation.subgraphDeploymentId,
_attestationData,
cancellableAt,
stakeSnapshot
);

Expand Down Expand Up @@ -473,6 +477,7 @@ contract DisputeManager is

// Store dispute
uint256 stakeSnapshot = _getStakeSnapshot(indexer, provision.tokens);
uint256 cancellableAt = block.timestamp + disputePeriod;
disputes[disputeId] = Dispute(
alloc.indexer,
_fisherman,
Expand All @@ -481,10 +486,20 @@ contract DisputeManager is
DisputeType.IndexingDispute,
IDisputeManager.DisputeStatus.Pending,
block.timestamp,
cancellableAt,
stakeSnapshot
);

emit IndexingDisputeCreated(disputeId, alloc.indexer, _fisherman, _deposit, _allocationId, _poi, stakeSnapshot);
emit IndexingDisputeCreated(
disputeId,
alloc.indexer,
_fisherman,
_deposit,
_allocationId,
_poi,
stakeSnapshot,
cancellableAt
);

return disputeId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ interface IDisputeManager {
* @param disputeType The type of dispute
* @param status The status of the dispute
* @param createdAt The timestamp when the dispute was created
* @param cancellableAt The timestamp when the dispute can be cancelled
* @param stakeSnapshot The stake snapshot of the indexer at the time of the dispute (includes delegation up to the delegation ratio)
*/
struct Dispute {
Expand All @@ -48,6 +49,7 @@ interface IDisputeManager {
DisputeType disputeType;
DisputeStatus status;
uint256 createdAt;
uint256 cancellableAt;
uint256 stakeSnapshot;
}

Expand Down Expand Up @@ -97,6 +99,7 @@ interface IDisputeManager {
* @param tokens The amount of tokens deposited by the fisherman
* @param subgraphDeploymentId The subgraph deployment id
* @param attestation The attestation
* @param cancellableAt The timestamp when the dispute can be cancelled
* @param stakeSnapshot The stake snapshot of the indexer at the time of the dispute
*/
event QueryDisputeCreated(
Expand All @@ -106,7 +109,8 @@ interface IDisputeManager {
uint256 tokens,
bytes32 subgraphDeploymentId,
bytes attestation,
uint256 stakeSnapshot
uint256 stakeSnapshot,
uint256 cancellableAt
);

/**
Expand All @@ -120,6 +124,7 @@ interface IDisputeManager {
* @param allocationId The allocation id
* @param poi The POI
* @param stakeSnapshot The stake snapshot of the indexer at the time of the dispute
* @param cancellableAt The timestamp when the dispute can be cancelled
*/
event IndexingDisputeCreated(
bytes32 indexed disputeId,
Expand All @@ -128,7 +133,8 @@ interface IDisputeManager {
uint256 tokens,
address allocationId,
bytes32 poi,
uint256 stakeSnapshot
uint256 stakeSnapshot,
uint256 cancellableAt
);

/**
Expand Down
16 changes: 11 additions & 5 deletions packages/subgraph-service/test/disputeManager/DisputeManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ contract DisputeManagerTest is SubgraphServiceSharedTest {
uint256 beforeFishermanBalance = token.balanceOf(fisherman);
Allocation.State memory alloc = subgraphService.getAllocation(_allocationId);
uint256 stakeSnapshot = disputeManager.getStakeSnapshot(alloc.indexer);
uint256 cancellableAt = block.timestamp + disputeManager.disputePeriod();

// Approve the dispute deposit
token.approve(address(disputeManager), disputeDeposit);
Expand All @@ -88,7 +89,8 @@ contract DisputeManagerTest is SubgraphServiceSharedTest {
disputeDeposit,
_allocationId,
_poi,
stakeSnapshot
stakeSnapshot,
cancellableAt
);

// Create the indexing dispute
Expand Down Expand Up @@ -144,6 +146,7 @@ contract DisputeManagerTest is SubgraphServiceSharedTest {
uint256 disputeDeposit = disputeManager.disputeDeposit();
uint256 beforeFishermanBalance = token.balanceOf(fisherman);
uint256 stakeSnapshot = disputeManager.getStakeSnapshot(indexer);
uint256 cancellableAt = block.timestamp + disputeManager.disputePeriod();

// Approve the dispute deposit
token.approve(address(disputeManager), disputeDeposit);
Expand All @@ -156,6 +159,7 @@ contract DisputeManagerTest is SubgraphServiceSharedTest {
disputeDeposit,
attestation.subgraphDeploymentId,
_attestationData,
cancellableAt,
stakeSnapshot
);

Expand Down Expand Up @@ -317,6 +321,8 @@ contract DisputeManagerTest is SubgraphServiceSharedTest {
)
);

uint256 cancellableAt = block.timestamp + disputeManager.disputePeriod();

// createQueryDisputeConflict
vm.expectEmit(address(disputeManager));
emit IDisputeManager.QueryDisputeCreated(
Expand All @@ -326,6 +332,7 @@ contract DisputeManagerTest is SubgraphServiceSharedTest {
disputeDeposit / 2,
beforeValues.attestation1.subgraphDeploymentId,
attestationData1,
cancellableAt,
beforeValues.stakeSnapshot1
);
vm.expectEmit(address(disputeManager));
Expand All @@ -336,6 +343,7 @@ contract DisputeManagerTest is SubgraphServiceSharedTest {
disputeDeposit / 2,
beforeValues.attestation2.subgraphDeploymentId,
attestationData2,
cancellableAt,
beforeValues.stakeSnapshot2
);

Expand Down Expand Up @@ -678,12 +686,8 @@ contract DisputeManagerTest is SubgraphServiceSharedTest {
if (isDisputeInConflict) relatedDispute = _getDispute(dispute.relatedDisputeId);
address fisherman = dispute.fisherman;
uint256 fishermanPreviousBalance = token.balanceOf(fisherman);
uint256 disputePeriod = disputeManager.disputePeriod();
uint256 indexerTokensAvailable = staking.getProviderTokensAvailable(dispute.indexer, address(subgraphService));

// skip to end of dispute period
skip(disputePeriod + 1);

vm.expectEmit(address(disputeManager));
emit IDisputeManager.DisputeCancelled(_disputeId, dispute.indexer, dispute.fisherman, dispute.deposit);

Expand Down Expand Up @@ -790,6 +794,7 @@ contract DisputeManagerTest is SubgraphServiceSharedTest {
IDisputeManager.DisputeType disputeType,
IDisputeManager.DisputeStatus status,
uint256 createdAt,
uint256 cancellableAt,
uint256 stakeSnapshot
) = disputeManager.disputes(_disputeId);
return
Expand All @@ -801,6 +806,7 @@ contract DisputeManagerTest is SubgraphServiceSharedTest {
disputeType: disputeType,
status: status,
createdAt: createdAt,
cancellableAt: cancellableAt,
stakeSnapshot: stakeSnapshot
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ contract DisputeManagerIndexingCancelDisputeTest is DisputeManagerTest {
resetPrank(users.fisherman);
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"));

// skip to end of dispute period
uint256 disputePeriod = disputeManager.disputePeriod();
skip(disputePeriod + 1);

_cancelDispute(disputeID);
}

Expand All @@ -38,4 +42,39 @@ contract DisputeManagerIndexingCancelDisputeTest is DisputeManagerTest {
vm.expectRevert(abi.encodeWithSelector(IDisputeManager.DisputeManagerDisputePeriodNotFinished.selector));
disputeManager.cancelDispute(disputeID);
}

function test_Indexing_Cancel_After_DisputePeriodIncreased(uint256 tokens) public useIndexer useAllocation(tokens) {
resetPrank(users.fisherman);
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"));

// change the dispute period to a higher value
uint256 oldDisputePeriod = disputeManager.disputePeriod();
resetPrank(users.governor);
disputeManager.setDisputePeriod(uint64(oldDisputePeriod * 2));

// skip to end of old dispute period
skip(oldDisputePeriod + 1);

// should be able to cancel
resetPrank(users.fisherman);
_cancelDispute(disputeID);
}

function test_Indexing_Cancel_After_DisputePeriodDecreased(uint256 tokens) public useIndexer useAllocation(tokens) {
resetPrank(users.fisherman);
bytes32 disputeID = _createIndexingDispute(allocationID, bytes32("POI1"));

// change the dispute period to a lower value
uint256 oldDisputePeriod = disputeManager.disputePeriod();
resetPrank(users.governor);
disputeManager.setDisputePeriod(uint64(oldDisputePeriod / 2));

// skip to end of new dispute period
skip(oldDisputePeriod / 2 + 1);

// should not be able to cancel
resetPrank(users.fisherman);
vm.expectRevert(abi.encodeWithSelector(IDisputeManager.DisputeManagerDisputePeriodNotFinished.selector));
disputeManager.cancelDispute(disputeID);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ contract DisputeManagerQueryCancelDisputeTest is DisputeManagerTest {
bytes memory attestationData = _createAtestationData(receipt, allocationIDPrivateKey);
bytes32 disputeID = _createQueryDispute(attestationData);

// skip to end of dispute period
uint256 disputePeriod = disputeManager.disputePeriod();
skip(disputePeriod + 1);

_cancelDispute(disputeID);
}

Expand All @@ -45,4 +49,43 @@ contract DisputeManagerQueryCancelDisputeTest is DisputeManagerTest {
vm.expectRevert(abi.encodeWithSelector(IDisputeManager.DisputeManagerDisputePeriodNotFinished.selector));
disputeManager.cancelDispute(disputeID);
}

function test_Query_Cancel_After_DisputePeriodIncreased(uint256 tokens) public useIndexer useAllocation(tokens) {
resetPrank(users.fisherman);
Attestation.Receipt memory receipt = _createAttestationReceipt(requestCID, responseCID, subgraphDeploymentId);
bytes memory attestationData = _createAtestationData(receipt, allocationIDPrivateKey);
bytes32 disputeID = _createQueryDispute(attestationData);

// change the dispute period to a higher value
uint256 oldDisputePeriod = disputeManager.disputePeriod();
resetPrank(users.governor);
disputeManager.setDisputePeriod(uint64(oldDisputePeriod * 2));

// skip to end of old dispute period
skip(oldDisputePeriod + 1);

// should be able to cancel
resetPrank(users.fisherman);
_cancelDispute(disputeID);
}

function test_Query_Cancel_After_DisputePeriodDecreased(uint256 tokens) public useIndexer useAllocation(tokens) {
resetPrank(users.fisherman);
Attestation.Receipt memory receipt = _createAttestationReceipt(requestCID, responseCID, subgraphDeploymentId);
bytes memory attestationData = _createAtestationData(receipt, allocationIDPrivateKey);
bytes32 disputeID = _createQueryDispute(attestationData);

// change the dispute period to a lower value
uint256 oldDisputePeriod = disputeManager.disputePeriod();
resetPrank(users.governor);
disputeManager.setDisputePeriod(uint64(oldDisputePeriod / 2));

// skip to end of new dispute period
skip(oldDisputePeriod / 2 + 1);

// should not be able to cancel
resetPrank(users.fisherman);
vm.expectRevert(abi.encodeWithSelector(IDisputeManager.DisputeManagerDisputePeriodNotFinished.selector));
disputeManager.cancelDispute(disputeID);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ contract DisputeManagerQueryConflictCancelDisputeTest is DisputeManagerTest {
resetPrank(users.fisherman);
(bytes32 disputeID1, ) = _createQueryDisputeConflict(attestationData1, attestationData2);

// skip to end of dispute period
uint256 disputePeriod = disputeManager.disputePeriod();
skip(disputePeriod + 1);

_cancelDispute(disputeID1);
}

Expand Down Expand Up @@ -70,4 +74,60 @@ contract DisputeManagerQueryConflictCancelDisputeTest is DisputeManagerTest {
vm.expectRevert(abi.encodeWithSelector(IDisputeManager.DisputeManagerDisputePeriodNotFinished.selector));
disputeManager.cancelDispute(disputeID1);
}

function test_Query_Conflict_Cancel_After_DisputePeriodIncreased(
uint256 tokens
) public useIndexer useAllocation(tokens) {
resetPrank(users.fisherman);
(bytes memory attestationData1, bytes memory attestationData2) = _createConflictingAttestations(
requestCID,
subgraphDeployment,
responseCID1,
responseCID2,
allocationIDPrivateKey,
allocationIDPrivateKey
);

resetPrank(users.fisherman);
(bytes32 disputeID1, ) = _createQueryDisputeConflict(attestationData1, attestationData2);

// change the dispute period to a higher value
uint256 oldDisputePeriod = disputeManager.disputePeriod();
resetPrank(users.governor);
disputeManager.setDisputePeriod(uint64(oldDisputePeriod * 2));

// skip to end of old dispute period
skip(oldDisputePeriod + 1);

// should be able to cancel
resetPrank(users.fisherman);
_cancelDispute(disputeID1);
}

function test_Query_Cancel_After_DisputePeriodDecreased(uint256 tokens) public useIndexer useAllocation(tokens) {
(bytes memory attestationData1, bytes memory attestationData2) = _createConflictingAttestations(
requestCID,
subgraphDeployment,
responseCID1,
responseCID2,
allocationIDPrivateKey,
allocationIDPrivateKey
);

resetPrank(users.fisherman);
(bytes32 disputeID1, ) = _createQueryDisputeConflict(attestationData1, attestationData2);

// change the dispute period to a lower value
uint256 oldDisputePeriod = disputeManager.disputePeriod();
resetPrank(users.governor);
disputeManager.setDisputePeriod(uint64(oldDisputePeriod / 2));

// skip to end of new dispute period
skip(oldDisputePeriod / 2 + 1);

// should not be able to cancel
resetPrank(users.fisherman);
vm.expectRevert(abi.encodeWithSelector(IDisputeManager.DisputeManagerDisputePeriodNotFinished.selector));
disputeManager.cancelDispute(disputeID1);
}
}