Skip to content

Commit 1e7d185

Browse files
fix(DK): withdraw local round bug
1 parent 536e61c commit 1e7d185

File tree

2 files changed

+21
-13
lines changed

2 files changed

+21
-13
lines changed

contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
2525
Round[] rounds; // Rounds of the dispute. 0 is the default round, and [1, ..n] are the appeal rounds.
2626
uint256 numberOfChoices; // The number of choices jurors have when voting. This does not include choice `0` which is reserved for "refuse to arbitrate".
2727
bool jumped; // True if dispute jumped to a parent dispute kit and won't be handled by this DK anymore.
28-
mapping(uint256 => uint256) coreRoundIDToLocal; // Maps id of the round in the core contract to the index of the round of related local dispute.
28+
mapping(uint256 => uint256) coreRoundIDToLocal; // Maps id of the round in the core contract to the length of the rounds array after the creation of a corresponding local dispute. Note we rely on length to skip the default 0 index.
2929
bytes extraData; // Extradata for the dispute.
3030
uint256[10] __gap; // Reserved slots for future upgrades.
3131
}
@@ -222,12 +222,13 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
222222

223223
dispute.numberOfChoices = _numberOfChoices;
224224
dispute.extraData = _extraData;
225-
// New round in the Core should be created before the dispute creation in DK.
226-
dispute.coreRoundIDToLocal[core.getNumberOfRounds(_coreDisputeID) - 1] = dispute.rounds.length;
227225

228226
Round storage round = dispute.rounds.push();
229227
round.tied = true;
230228

229+
// New round in the Core should be created before the dispute creation in DK.
230+
dispute.coreRoundIDToLocal[core.getNumberOfRounds(_coreDisputeID) - 1] = dispute.rounds.length;
231+
231232
emit DisputeCreation(_coreDisputeID, _numberOfChoices, _extraData);
232233
}
233234

@@ -428,7 +429,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
428429
dispute.jumped = true;
429430
} else {
430431
// Don't subtract 1 from length since both round arrays haven't been updated yet.
431-
dispute.coreRoundIDToLocal[coreRoundID + 1] = dispute.rounds.length;
432+
dispute.coreRoundIDToLocal[coreRoundID + 1] = dispute.rounds.length + 1;
432433

433434
Round storage newRound = dispute.rounds.push();
434435
newRound.tied = true;
@@ -458,7 +459,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
458459
if (!coreDisputeIDToActive[_coreDisputeID]) revert NotActiveForCoreDisputeID();
459460

460461
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
461-
Round storage round = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID]];
462+
Round storage round = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID] - 1];
462463
(uint256 finalRuling, , ) = core.currentRuling(_coreDisputeID);
463464

464465
if (!round.hasPaid[_choice]) {
@@ -562,7 +563,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
562563
) internal view returns (uint256 coherence) {
563564
// In this contract this degree can be either 0 or 1, but in other dispute kits this value can be something in between.
564565
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
565-
Vote storage vote = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID]].votes[_voteID];
566+
Vote storage vote = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID] - 1].votes[_voteID];
566567
(uint256 winningChoice, bool tied, ) = core.currentRuling(_coreDisputeID);
567568

568569
if (vote.voted && (vote.choice == winningChoice || tied)) {
@@ -575,7 +576,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
575576
/// @inheritdoc IDisputeKit
576577
function getCoherentCount(uint256 _coreDisputeID, uint256 _coreRoundID) external view override returns (uint256) {
577578
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
578-
Round storage currentRound = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID]];
579+
Round storage currentRound = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID] - 1];
579580
(uint256 winningChoice, bool tied, ) = core.currentRuling(_coreDisputeID);
580581

581582
if (currentRound.totalVoted == 0 || (!tied && currentRound.counts[winningChoice] == 0)) {
@@ -643,7 +644,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
643644
uint256 _voteID
644645
) external view override returns (bool) {
645646
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
646-
Vote storage vote = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID]].votes[_voteID];
647+
Vote storage vote = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID] - 1].votes[_voteID];
647648
return vote.voted;
648649
}
649650

@@ -666,7 +667,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
666667
)
667668
{
668669
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
669-
Round storage round = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID]];
670+
Round storage round = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID] - 1];
670671
return (
671672
round.winningChoice,
672673
round.tied,
@@ -694,7 +695,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
694695
uint256 _coreRoundID
695696
) external view returns (uint256 localDisputeID, uint256 localRoundID) {
696697
localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
697-
localRoundID = disputes[localDisputeID].coreRoundIDToLocal[_coreRoundID];
698+
localRoundID = disputes[localDisputeID].coreRoundIDToLocal[_coreRoundID] - 1;
698699
}
699700

700701
/// @inheritdoc IDisputeKit
@@ -704,7 +705,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
704705
uint256 _voteID
705706
) external view override returns (address account, bytes32 commit, uint256 choice, bool voted) {
706707
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
707-
Vote storage vote = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID]].votes[_voteID];
708+
Vote storage vote = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID] - 1].votes[_voteID];
708709
return (vote.account, vote.commit, vote.choice, vote.voted);
709710
}
710711

contracts/test/foundry/KlerosCore_Appeals.t.sol

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
pragma solidity ^0.8.24;
33

44
import {KlerosCore_TestBase} from "./KlerosCore_TestBase.sol";
5+
import {stdError} from "forge-std/Test.sol";
56
import {KlerosCore} from "../../src/arbitration/KlerosCore.sol";
67
import {DisputeKitClassic, DisputeKitClassicBase} from "../../src/arbitration/dispute-kits/DisputeKitClassic.sol";
78
import {UUPSProxy} from "../../src/proxy/UUPSProxy.sol";
@@ -762,15 +763,19 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase {
762763
vm.expectEmit();
763764
emit DisputeKitClassicBase.Withdrawal(disputeID, 0, 2, payable(crowdfunder2), 0.84 ether);
764765
disputeKit3.withdrawFeesAndRewards(disputeID, payable(crowdfunder2), 0, 2); // REWARDS
766+
vm.expectRevert(stdError.arithmeticError); // underflow
765767
disputeKit2.withdrawFeesAndRewards(disputeID, payable(crowdfunder1), 0, 1); // wrong DK, no reward
768+
vm.expectRevert(stdError.arithmeticError); // underflow
766769
disputeKit2.withdrawFeesAndRewards(disputeID, payable(crowdfunder2), 0, 2); // wrong DK, no reward
767770
vm.expectRevert(DisputeKitClassicBase.NotActiveForCoreDisputeID.selector);
768771
disputeKit.withdrawFeesAndRewards(disputeID, payable(crowdfunder1), 0, 1); // wrong DK, no reward
769772
vm.expectRevert(DisputeKitClassicBase.NotActiveForCoreDisputeID.selector);
770773
disputeKit.withdrawFeesAndRewards(disputeID, payable(crowdfunder2), 0, 2); // wrong DK, no reward
771774

772775
// Appeal Rewards for Round 2 //
776+
vm.expectRevert(stdError.arithmeticError); // underflow
773777
disputeKit3.withdrawFeesAndRewards(disputeID, payable(crowdfunder1), 1, 1); // wrong DK, no reward
778+
vm.expectRevert(stdError.arithmeticError); // underflow
774779
disputeKit3.withdrawFeesAndRewards(disputeID, payable(crowdfunder2), 1, 2); // wrong DK, no reward
775780
disputeKit2.withdrawFeesAndRewards(disputeID, payable(crowdfunder1), 1, 1); // wrong side, no reward
776781
vm.expectEmit();
@@ -784,8 +789,10 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase {
784789
// Appeal Rewards for Round 3 //
785790
disputeKit3.withdrawFeesAndRewards(disputeID, payable(crowdfunder1), 2, 1); // no appeal, no reward
786791
disputeKit3.withdrawFeesAndRewards(disputeID, payable(crowdfunder2), 2, 2); // no appeal, no reward
787-
disputeKit2.withdrawFeesAndRewards(disputeID, payable(crowdfunder1), 2, 1); // no appeal, no reward
788-
disputeKit2.withdrawFeesAndRewards(disputeID, payable(crowdfunder2), 2, 2); // no appeal, no reward
792+
vm.expectRevert(stdError.arithmeticError); // underflow
793+
disputeKit2.withdrawFeesAndRewards(disputeID, payable(crowdfunder1), 2, 1); // wrong DK, no reward
794+
vm.expectRevert(stdError.arithmeticError); // underflow
795+
disputeKit2.withdrawFeesAndRewards(disputeID, payable(crowdfunder2), 2, 2); // wrong DK, no reward
789796
vm.expectRevert(DisputeKitClassicBase.NotActiveForCoreDisputeID.selector);
790797
disputeKit.withdrawFeesAndRewards(disputeID, payable(crowdfunder1), 2, 1); // wrong DK, no reward
791798
vm.expectRevert(DisputeKitClassicBase.NotActiveForCoreDisputeID.selector);

0 commit comments

Comments
 (0)