@@ -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 */
2725contract 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