Skip to content

Commit d574ce8

Browse files
authored
Merge pull request #1955 from kleros/feat/dispute-period-quick-pass
feat(DK): pass commit and appeal quicker
2 parents e1b8ce2 + 5f8e25c commit d574ce8

File tree

4 files changed

+125
-2
lines changed

4 files changed

+125
-2
lines changed

Diff for: contracts/src/arbitration/KlerosCoreBase.sol

+4-1
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,10 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
575575
dispute.period = Period.appeal;
576576
emit AppealPossible(_disputeID, dispute.arbitrated);
577577
} else if (dispute.period == Period.appeal) {
578-
if (block.timestamp - dispute.lastPeriodChange < court.timesPerPeriod[uint256(dispute.period)]) {
578+
if (
579+
block.timestamp - dispute.lastPeriodChange < court.timesPerPeriod[uint256(dispute.period)] &&
580+
!disputeKits[round.disputeKitID].isAppealFunded(_disputeID)
581+
) {
579582
revert AppealPeriodNotPassed();
580583
}
581584
dispute.period = Period.execution;

Diff for: contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol

+21-1
Original file line numberDiff line numberDiff line change
@@ -517,12 +517,32 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
517517
}
518518

519519
/// @dev Returns true if all of the jurors have cast their votes for the last round.
520+
/// Note that this function is to be called directly by the core contract and is not for off-chain usage.
520521
/// @param _coreDisputeID The ID of the dispute in Kleros Core.
521522
/// @return Whether all of the jurors have cast their votes for the last round.
522523
function areVotesAllCast(uint256 _coreDisputeID) external view override returns (bool) {
523524
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
524525
Round storage round = dispute.rounds[dispute.rounds.length - 1];
525-
return round.totalVoted == round.votes.length;
526+
527+
(uint96 courtID, , , , ) = core.disputes(_coreDisputeID);
528+
(, bool hiddenVotes, , , , , ) = core.courts(courtID);
529+
uint256 expectedTotalVoted = hiddenVotes ? round.totalCommitted : round.votes.length;
530+
531+
return round.totalVoted == expectedTotalVoted;
532+
}
533+
534+
/// @dev Returns true if the appeal funding is finished prematurely (e.g. when losing side didn't fund).
535+
/// Note that this function is to be called directly by the core contract and is not for off-chain usage.
536+
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
537+
/// @return Whether the appeal funding is finished.
538+
function isAppealFunded(uint256 _coreDisputeID) external view override returns (bool) {
539+
(uint256 appealPeriodStart, uint256 appealPeriodEnd) = core.appealPeriod(_coreDisputeID);
540+
541+
uint256[] memory fundedChoices = getFundedChoices(_coreDisputeID);
542+
// Uses block.timestamp from the current tx when called by the core contract.
543+
return (fundedChoices.length == 0 &&
544+
block.timestamp - appealPeriodStart >=
545+
((appealPeriodEnd - appealPeriodStart) * LOSER_APPEAL_PERIOD_MULTIPLIER) / ONE_BASIS_POINT);
526546
}
527547

528548
/// @dev Returns true if the specified voter was active in this round.

Diff for: contracts/src/arbitration/interfaces/IDisputeKit.sol

+5
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ interface IDisputeKit {
9292
/// @return Whether all of the jurors have cast their votes for the last round.
9393
function areVotesAllCast(uint256 _coreDisputeID) external view returns (bool);
9494

95+
/// @dev Returns true if the appeal funding is finished prematurely (e.g. when losing side didn't fund).
96+
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
97+
/// @return Whether the appeal funding is finished.
98+
function isAppealFunded(uint256 _coreDisputeID) external view returns (bool);
99+
95100
/// @dev Returns true if the specified voter was active in this round.
96101
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
97102
/// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit.

Diff for: contracts/test/foundry/KlerosCore.t.sol

+95
Original file line numberDiff line numberDiff line change
@@ -1801,6 +1801,9 @@ contract KlerosCoreTest is Test {
18011801
assertEq(totalVoted, 2, "totalVoted should be 2");
18021802
assertEq(choiceCount, 1, "choiceCount should be 1 for first choice");
18031803

1804+
vm.expectRevert(KlerosCoreBase.VotePeriodNotPassed.selector);
1805+
core.passPeriod(disputeID);
1806+
18041807
voteIDs = new uint256[](1);
18051808
voteIDs[0] = 2; // Cast another vote to declare a new winner.
18061809

@@ -1874,6 +1877,64 @@ contract KlerosCoreTest is Test {
18741877
assertEq(overridden, false, "Not overridden");
18751878
}
18761879

1880+
function test_castVote_quickPassPeriod() public {
1881+
// Change hidden votes in general court
1882+
uint256 disputeID = 0;
1883+
vm.prank(governor);
1884+
core.changeCourtParameters(
1885+
GENERAL_COURT,
1886+
true, // Hidden votes
1887+
1000, // min stake
1888+
10000, // alpha
1889+
0.03 ether, // fee for juror
1890+
511, // jurors for jump
1891+
[uint256(60), uint256(120), uint256(180), uint256(240)] // Times per period
1892+
);
1893+
1894+
vm.prank(staker1);
1895+
core.setStake(GENERAL_COURT, 10000);
1896+
vm.prank(disputer);
1897+
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
1898+
vm.warp(block.timestamp + minStakingTime);
1899+
sortitionModule.passPhase(); // Generating
1900+
vm.roll(block.number + rngLookahead + 1);
1901+
sortitionModule.passPhase(); // Drawing phase
1902+
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
1903+
1904+
uint256 YES = 1;
1905+
uint256 salt = 123455678;
1906+
uint256[] memory voteIDs = new uint256[](1);
1907+
voteIDs[0] = 0;
1908+
bytes32 commit;
1909+
1910+
vm.warp(block.timestamp + timesPerPeriod[0]);
1911+
core.passPeriod(disputeID);
1912+
1913+
commit = keccak256(abi.encodePacked(YES, salt));
1914+
1915+
vm.prank(staker1);
1916+
disputeKit.castCommit(disputeID, voteIDs, commit);
1917+
1918+
(, , , uint256 totalCommited, uint256 nbVoters, uint256 choiceCount) = disputeKit.getRoundInfo(disputeID, 0, 0);
1919+
assertEq(totalCommited, 1, "totalCommited should be 1");
1920+
assertEq(disputeKit.areCommitsAllCast(disputeID), false, "Commits should not all be cast");
1921+
1922+
vm.warp(block.timestamp + timesPerPeriod[1]);
1923+
core.passPeriod(disputeID);
1924+
1925+
vm.prank(staker1);
1926+
disputeKit.castVote(disputeID, voteIDs, YES, salt, "XYZ");
1927+
1928+
(, , uint256 totalVoted, , , ) = disputeKit.getRoundInfo(disputeID, 0, 0);
1929+
assertEq(totalVoted, 1, "totalVoted should be 1");
1930+
assertEq(disputeKit.areVotesAllCast(disputeID), true, "Every committed vote was cast");
1931+
1932+
// Should pass period by counting only committed votes.
1933+
vm.expectEmit(true, true, true, true);
1934+
emit KlerosCoreBase.NewPeriod(disputeID, KlerosCoreBase.Period.appeal);
1935+
core.passPeriod(disputeID);
1936+
}
1937+
18771938
function test_appeal_fundOneSide() public {
18781939
uint256 disputeID = 0;
18791940
vm.deal(address(disputeKit), 1 ether);
@@ -2184,6 +2245,40 @@ contract KlerosCoreTest is Test {
21842245
assertEq(account, staker1, "Wrong drawn account in the classic DK");
21852246
}
21862247

2248+
function test_appeal_quickPassPeriod() public {
2249+
uint256 disputeID = 0;
2250+
2251+
vm.prank(staker1);
2252+
core.setStake(GENERAL_COURT, 10000);
2253+
vm.prank(disputer);
2254+
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
2255+
vm.warp(block.timestamp + minStakingTime);
2256+
sortitionModule.passPhase(); // Generating
2257+
vm.roll(block.number + rngLookahead + 1);
2258+
sortitionModule.passPhase(); // Drawing phase
2259+
2260+
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
2261+
vm.warp(block.timestamp + timesPerPeriod[0]);
2262+
core.passPeriod(disputeID); // Vote
2263+
2264+
uint256[] memory voteIDs = new uint256[](3);
2265+
voteIDs[0] = 0;
2266+
voteIDs[1] = 1;
2267+
voteIDs[2] = 2;
2268+
2269+
vm.prank(staker1);
2270+
disputeKit.castVote(disputeID, voteIDs, 2, 0, "XYZ");
2271+
2272+
core.passPeriod(disputeID); // Appeal
2273+
2274+
vm.warp(block.timestamp + timesPerPeriod[3] / 2);
2275+
2276+
// Should pass to execution period without waiting for the 2nd half of the appeal.
2277+
vm.expectEmit(true, true, true, true);
2278+
emit KlerosCoreBase.NewPeriod(disputeID, KlerosCoreBase.Period.execution);
2279+
core.passPeriod(disputeID);
2280+
}
2281+
21872282
function test_execute() public {
21882283
uint256 disputeID = 0;
21892284

0 commit comments

Comments
 (0)