@@ -21,14 +21,18 @@ import "../../evidence/IEvidence.sol";
21
21
* - a vote aggreation system: plurality,
22
22
* - an incentive system: equal split between coherent votes,
23
23
* - 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.
26
24
*/
27
25
contract DisputeKitClassic is BaseDisputeKit , IEvidence {
28
26
// ************************************* //
29
27
// * Structs * //
30
28
// ************************************* //
31
29
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
+
32
36
struct Dispute {
33
37
Round[] rounds; // Rounds of the dispute. 0 is the default round, and [1, ..n] are the appeal rounds.
34
38
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 {
49
53
mapping (address => mapping (uint256 => uint256 )) contributions; // Maps contributors to their contributions for each choice.
50
54
uint256 feeRewards; // Sum of reimbursable appeal fees available to the parties that made contributions to the ruling that ultimately wins a dispute.
51
55
uint256 [] fundedChoices; // Stores the choices that are fully funded.
56
+ uint256 nbVotes; // Maximal number of votes this dispute can get.
52
57
}
53
58
54
59
struct Vote {
@@ -68,6 +73,10 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
68
73
uint256 public constant ONE_BASIS_POINT = 10000 ; // One basis point, for scaling.
69
74
70
75
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.
71
80
Dispute[] public disputes; // Array of the locally created disputes.
72
81
mapping (uint256 => uint256 ) public coreDisputeIDToLocal; // Maps the dispute ID in Kleros Core to the local dispute ID.
73
82
@@ -92,6 +101,7 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
92
101
);
93
102
94
103
event ChoiceFunded (uint256 indexed _disputeID , uint256 indexed _round , uint256 indexed _choice );
104
+ event NewPhaseDisputeKit (Phase _phase );
95
105
96
106
// ************************************* //
97
107
// * Modifiers * //
@@ -129,7 +139,7 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
129
139
/** @dev Changes the `core` storage variable.
130
140
* @param _core The new value for the `core` storage variable.
131
141
*/
132
- function changeCore (address payable _core ) external onlyByGovernor {
142
+ function changeCore (address _core ) external onlyByGovernor {
133
143
core = KlerosCore (_core);
134
144
}
135
145
@@ -150,23 +160,55 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
150
160
* @param _coreDisputeID The ID of the dispute in Kleros Core.
151
161
* @param _numberOfChoices Number of choices of the dispute
152
162
* @param _extraData Additional info about the dispute, for possible use in future dispute kits.
163
+ * @param _nbVotes Number of votes for this dispute.
153
164
*/
154
165
function createDispute (
155
166
uint256 _coreDisputeID ,
156
167
uint256 _numberOfChoices ,
157
- bytes calldata _extraData
168
+ bytes calldata _extraData ,
169
+ uint256 _nbVotes
158
170
) external override onlyByCore {
159
171
uint256 localDisputeID = disputes.length ;
160
172
Dispute storage dispute = disputes.push ();
161
173
dispute.numberOfChoices = _numberOfChoices;
162
174
dispute.extraData = _extraData;
175
+
163
176
// New round in the Core should be created before the dispute creation in DK.
164
177
dispute.coreRoundIDToLocal[core.getNumberOfRounds (_coreDisputeID) - 1 ] = dispute.rounds.length ;
165
178
166
179
Round storage round = dispute.rounds.push ();
180
+ round.nbVotes = _nbVotes;
167
181
round.tied = true ;
168
182
169
183
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);
170
212
}
171
213
172
214
/** @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 {
181
223
notJumped (_coreDisputeID)
182
224
returns (address drawnAddress )
183
225
{
226
+ require (phase == Phase.drawing, "Should be in drawing phase " );
184
227
bytes32 key = bytes32 (core.getSubcourtID (_coreDisputeID)); // Get the ID of the tree.
185
228
uint256 drawnNumber = getRandomNumber ();
186
229
@@ -214,7 +257,14 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
214
257
bytes32 ID = core.getSortitionSumTreeID (key, treeIndex);
215
258
drawnAddress = stakePathIDToAccount (ID);
216
259
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
+ }
218
268
}
219
269
220
270
/** @dev Sets the caller's commit for the specified votes.
@@ -352,15 +402,17 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
352
402
// At least two sides are fully funded.
353
403
round.feeRewards = round.feeRewards - appealCost;
354
404
355
- // Don't create a new round in case of a jump, and remove local dispute from the flow.
356
405
if (core.isDisputeKitJumping (_coreDisputeID)) {
406
+ // Don't create a new round in case of a jump, and remove local dispute from the flow.
357
407
dispute.jumped = true ;
358
408
} else {
359
409
// Don't subtract 1 from length since both round arrays haven't been updated yet.
360
410
dispute.coreRoundIDToLocal[core.getNumberOfRounds (_coreDisputeID)] = dispute.rounds.length ;
361
411
362
412
Round storage newRound = dispute.rounds.push ();
413
+ newRound.nbVotes = core.getNumberOfVotes (_coreDisputeID);
363
414
newRound.tied = true ;
415
+ disputesWithoutJurors++ ;
364
416
}
365
417
core.appeal {value: appealCost}(_coreDisputeID, dispute.numberOfChoices, dispute.extraData);
366
418
}
@@ -572,10 +624,24 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
572
624
return (vote.account, vote.commit, vote.choice, vote.voted);
573
625
}
574
626
627
+ function isResolving () external view override returns (bool ) {
628
+ return phase == Phase.resolving;
629
+ }
630
+
575
631
// ************************************* //
576
632
// * Internal * //
577
633
// ************************************* //
578
634
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
+
579
645
/** @dev RNG function
580
646
* @return rn A random number.
581
647
*/
0 commit comments