Skip to content

Commit 263bd18

Browse files
authored
Merge pull request #65 from kleros/feat/arbitration-phases
feat: phases implementation
2 parents 15ae9db + b42d718 commit 263bd18

File tree

8 files changed

+515
-79
lines changed

8 files changed

+515
-79
lines changed

contracts/deploy/00-home-chain-arbitration.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,17 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
5959
libraries: {
6060
SortitionSumTreeFactory: sortitionSumTreeLibrary.address,
6161
},
62-
args: [deployer, pnk, AddressZero, disputeKit.address, false, minStake, alpha, feeForJuror, 3, [0, 0, 0, 0], 3],
62+
args: [
63+
deployer,
64+
pnk,
65+
AddressZero,
66+
disputeKit.address,
67+
[120, 120], // minStakingTime, maxFreezingTime
68+
false,
69+
[minStake, alpha, feeForJuror, 3], // minStake, alpha, feeForJuror, jurorsForCourtJump
70+
[0, 0, 0, 0], // evidencePeriod, commitPeriod, votePeriod, appealPeriod
71+
3,
72+
],
6373
log: true,
6474
});
6575

contracts/src/arbitration/IDisputeKit.sol

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,14 @@ interface IDisputeKit {
3131
function createDispute(
3232
uint256 _coreDisputeID,
3333
uint256 _numberOfChoices,
34-
bytes calldata _extraData
34+
bytes calldata _extraData,
35+
uint256 _nbVotes
3536
) external;
3637

38+
/** @dev Passes the phase.
39+
*/
40+
function passPhase() external;
41+
3742
/** @dev Draws the juror from the sortition tree. The drawn address is picked up by Kleros Core.
3843
* Note: Access restricted to Kleros Core only.
3944
* @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
@@ -130,4 +135,14 @@ interface IDisputeKit {
130135
uint256 choice,
131136
bool voted
132137
);
138+
139+
/** @dev Returns the number of disputes without jurors in the dispute kit.
140+
* @return The number of disputes without jurors in the dispute kit.
141+
*/
142+
function disputesWithoutJurors() external view returns (uint256);
143+
144+
/** @dev Returns true if the dispute kit is ready to Resolve, regardless of the number of disputes without jurors.
145+
* @return Whether the dispute kit is ready to resolve, regardless of the number of disputes without jurors.
146+
*/
147+
function isResolving() external view returns (bool);
133148
}

contracts/src/arbitration/KlerosCore.sol

Lines changed: 206 additions & 47 deletions
Large diffs are not rendered by default.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,10 @@ abstract contract BaseDisputeKit is IDisputeKit {
6565
(bool success, ) = _destination.call{value: _amount}(_data);
6666
require(success, "Unsuccessful call");
6767
}
68+
69+
/** @dev Checks that the chosen address satisfies certain conditions for being drawn.
70+
* @param _disputeID ID of the dispute in the core contract.
71+
* @param _juror Chosen address.
72+
*/
73+
function postDrawCheck(uint256 _disputeID, address _juror) internal virtual returns (bool);
6874
}

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

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,18 @@ import "../../evidence/IEvidence.sol";
2121
* - a vote aggreation system: plurality,
2222
* - an incentive system: equal split between coherent votes,
2323
* - an appeal system: fund 2 choices only, vote on any choice.
24-
* TODO:
25-
* - phase management: Generating->Drawing->Resolving->Generating in coordination with KlerosCore to freeze staking.
2624
*/
2725
contract DisputeKitClassic is BaseDisputeKit, IEvidence {
2826
// ************************************* //
2927
// * Structs * //
3028
// ************************************* //
3129

30+
enum Phase {
31+
resolving, // No disputes that need drawing.
32+
generating, // Waiting for a random number. Pass as soon as it is ready.
33+
drawing // Jurors can be drawn.
34+
}
35+
3236
struct Dispute {
3337
Round[] rounds; // Rounds of the dispute. 0 is the default round, and [1, ..n] are the appeal rounds.
3438
uint256 numberOfChoices; // The number of choices jurors have when voting. This does not include choice `0` which is reserved for "refuse to arbitrate".
@@ -49,6 +53,7 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
4953
mapping(address => mapping(uint256 => uint256)) contributions; // Maps contributors to their contributions for each choice.
5054
uint256 feeRewards; // Sum of reimbursable appeal fees available to the parties that made contributions to the ruling that ultimately wins a dispute.
5155
uint256[] fundedChoices; // Stores the choices that are fully funded.
56+
uint256 nbVotes; // Maximal number of votes this dispute can get.
5257
}
5358

5459
struct Vote {
@@ -68,6 +73,10 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
6873
uint256 public constant ONE_BASIS_POINT = 10000; // One basis point, for scaling.
6974

7075
RNG public rng; // The random number generator
76+
uint256 public RNBlock; // The block number when the random number was requested.
77+
uint256 public RN; // The current random number.
78+
Phase public phase; // Current phase of this dispute kit.
79+
uint256 public disputesWithoutJurors; // The number of disputes that have not finished drawing jurors.
7180
Dispute[] public disputes; // Array of the locally created disputes.
7281
mapping(uint256 => uint256) public coreDisputeIDToLocal; // Maps the dispute ID in Kleros Core to the local dispute ID.
7382

@@ -92,6 +101,7 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
92101
);
93102

94103
event ChoiceFunded(uint256 indexed _disputeID, uint256 indexed _round, uint256 indexed _choice);
104+
event NewPhaseDisputeKit(Phase _phase);
95105

96106
// ************************************* //
97107
// * Modifiers * //
@@ -129,7 +139,7 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
129139
/** @dev Changes the `core` storage variable.
130140
* @param _core The new value for the `core` storage variable.
131141
*/
132-
function changeCore(address payable _core) external onlyByGovernor {
142+
function changeCore(address _core) external onlyByGovernor {
133143
core = KlerosCore(_core);
134144
}
135145

@@ -150,23 +160,55 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
150160
* @param _coreDisputeID The ID of the dispute in Kleros Core.
151161
* @param _numberOfChoices Number of choices of the dispute
152162
* @param _extraData Additional info about the dispute, for possible use in future dispute kits.
163+
* @param _nbVotes Number of votes for this dispute.
153164
*/
154165
function createDispute(
155166
uint256 _coreDisputeID,
156167
uint256 _numberOfChoices,
157-
bytes calldata _extraData
168+
bytes calldata _extraData,
169+
uint256 _nbVotes
158170
) external override onlyByCore {
159171
uint256 localDisputeID = disputes.length;
160172
Dispute storage dispute = disputes.push();
161173
dispute.numberOfChoices = _numberOfChoices;
162174
dispute.extraData = _extraData;
175+
163176
// New round in the Core should be created before the dispute creation in DK.
164177
dispute.coreRoundIDToLocal[core.getNumberOfRounds(_coreDisputeID) - 1] = dispute.rounds.length;
165178

166179
Round storage round = dispute.rounds.push();
180+
round.nbVotes = _nbVotes;
167181
round.tied = true;
168182

169183
coreDisputeIDToLocal[_coreDisputeID] = localDisputeID;
184+
disputesWithoutJurors++;
185+
}
186+
187+
/** @dev Passes the phase.
188+
*/
189+
function passPhase() external override {
190+
if (core.phase() == KlerosCore.Phase.staking || core.freezingPhaseTimeout()) {
191+
require(phase != Phase.resolving, "Already in Resolving phase");
192+
phase = Phase.resolving; // Safety net.
193+
} else if (core.phase() == KlerosCore.Phase.freezing) {
194+
if (phase == Phase.resolving) {
195+
require(disputesWithoutJurors > 0, "All the disputes have jurors");
196+
require(block.number >= core.getFreezeBlock() + 20, "Too soon: L1 finality required");
197+
// TODO: RNG process is currently unfinished.
198+
RNBlock = block.number;
199+
rng.requestRN(block.number);
200+
phase = Phase.generating;
201+
} else if (phase == Phase.generating) {
202+
RN = rng.getRN(RNBlock);
203+
require(RN != 0, "Random number is not ready yet");
204+
phase = Phase.drawing;
205+
} else if (phase == Phase.drawing) {
206+
require(disputesWithoutJurors == 0, "Not ready for Resolving phase");
207+
phase = Phase.resolving;
208+
}
209+
}
210+
// Should not be reached if the phase is unchanged.
211+
emit NewPhaseDisputeKit(phase);
170212
}
171213

172214
/** @dev Draws the juror from the sortition tree. The drawn address is picked up by Kleros Core.
@@ -181,6 +223,7 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
181223
notJumped(_coreDisputeID)
182224
returns (address drawnAddress)
183225
{
226+
require(phase == Phase.drawing, "Should be in drawing phase");
184227
bytes32 key = bytes32(core.getSubcourtID(_coreDisputeID)); // Get the ID of the tree.
185228
uint256 drawnNumber = getRandomNumber();
186229

@@ -214,7 +257,14 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
214257
bytes32 ID = core.getSortitionSumTreeID(key, treeIndex);
215258
drawnAddress = stakePathIDToAccount(ID);
216259

217-
round.votes.push(Vote({account: drawnAddress, commit: bytes32(0), choice: 0, voted: false}));
260+
if (postDrawCheck(_coreDisputeID, drawnAddress)) {
261+
round.votes.push(Vote({account: drawnAddress, commit: bytes32(0), choice: 0, voted: false}));
262+
if (round.votes.length == round.nbVotes) {
263+
disputesWithoutJurors--;
264+
}
265+
} else {
266+
drawnAddress = address(0);
267+
}
218268
}
219269

220270
/** @dev Sets the caller's commit for the specified votes.
@@ -352,15 +402,17 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
352402
// At least two sides are fully funded.
353403
round.feeRewards = round.feeRewards - appealCost;
354404

355-
// Don't create a new round in case of a jump, and remove local dispute from the flow.
356405
if (core.isDisputeKitJumping(_coreDisputeID)) {
406+
// Don't create a new round in case of a jump, and remove local dispute from the flow.
357407
dispute.jumped = true;
358408
} else {
359409
// Don't subtract 1 from length since both round arrays haven't been updated yet.
360410
dispute.coreRoundIDToLocal[core.getNumberOfRounds(_coreDisputeID)] = dispute.rounds.length;
361411

362412
Round storage newRound = dispute.rounds.push();
413+
newRound.nbVotes = core.getNumberOfVotes(_coreDisputeID);
363414
newRound.tied = true;
415+
disputesWithoutJurors++;
364416
}
365417
core.appeal{value: appealCost}(_coreDisputeID, dispute.numberOfChoices, dispute.extraData);
366418
}
@@ -572,10 +624,24 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
572624
return (vote.account, vote.commit, vote.choice, vote.voted);
573625
}
574626

627+
function isResolving() external view override returns (bool) {
628+
return phase == Phase.resolving;
629+
}
630+
575631
// ************************************* //
576632
// * Internal * //
577633
// ************************************* //
578634

635+
function postDrawCheck(uint256 _coreDisputeID, address _juror) internal view override returns (bool) {
636+
uint256 subcourtID = core.getSubcourtID(_coreDisputeID);
637+
(uint256 lockedAmountPerJuror, , , , , ) = core.getRoundInfo(
638+
_coreDisputeID,
639+
core.getNumberOfRounds(_coreDisputeID) - 1
640+
);
641+
(uint256 stakedTokens, uint256 lockedTokens) = core.getJurorBalance(_juror, uint96(subcourtID));
642+
return stakedTokens >= lockedTokens + lockedAmountPerJuror;
643+
}
644+
579645
/** @dev RNG function
580646
* @return rn A random number.
581647
*/

0 commit comments

Comments
 (0)