Skip to content

Commit 98d903c

Browse files
authored
Merge pull request #71 from kleros/feat/dispute-kit-forest
Feat: add iterations for DK jump + hidden votes setter
2 parents b70f8b2 + 10f9d59 commit 98d903c

File tree

1 file changed

+70
-41
lines changed

1 file changed

+70
-41
lines changed

contracts/src/arbitration/KlerosCore.sol

Lines changed: 70 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ contract KlerosCore is IArbitrator {
7373

7474
struct DisputeKitNode {
7575
uint256 parent; // Index of the parent dispute kit. If it's 0 then this DK is a root.
76+
uint256[] children; // List of child dispute kits.
7677
IDisputeKit disputeKit; // The dispute kit implementation.
78+
uint256 depthLevel; // How far this DK is from the root. 0 for root DK.
7779
}
7880

7981
// ************************************* //
@@ -87,6 +89,7 @@ contract KlerosCore is IArbitrator {
8789
uint256 public constant MIN_JURORS = 3; // The global default minimum number of jurors in a dispute.
8890
uint256 public constant ALPHA_DIVISOR = 1e4; // The number to divide `Court.alpha` by.
8991
uint256 public constant NON_PAYABLE_AMOUNT = (2**256 - 2) / 2; // An amount higher than the supply of ETH.
92+
uint256 public constant SEARCH_ITERATIONS = 10; // Number of iterations to search for suitable parent court before jumping to the top court.
9093

9194
address public governor; // The governor of the contract.
9295
IERC20 public pinakion; // The Pinakion token contract.
@@ -166,7 +169,9 @@ contract KlerosCore is IArbitrator {
166169
jurorProsecutionModule = _jurorProsecutionModule;
167170

168171
disputeKitNodes.push(); // NULL_DISPUTE_KIT: an empty element at index 0 to indicate when a node has no parent.
169-
disputeKitNodes.push(DisputeKitNode({parent: 0, disputeKit: _disputeKit}));
172+
disputeKitNodes.push(
173+
DisputeKitNode({parent: 0, children: new uint256[](0), disputeKit: _disputeKit, depthLevel: 0})
174+
);
170175

171176
// Create the Forking court.
172177
Court storage court = courts.push();
@@ -228,30 +233,28 @@ contract KlerosCore is IArbitrator {
228233
* Note that the root DK must be supported by the forking court.
229234
*/
230235
function addNewDisputeKit(IDisputeKit _disputeKitAddress, uint256 _parent) external onlyByGovernor {
231-
require(_parent < disputeKitNodes.length, "Parent doesn't exist");
236+
uint256 disputeKitID = disputeKitNodes.length;
237+
require(_parent < disputeKitID, "Parent doesn't exist");
238+
uint256 depthLevel;
232239

233240
// Create new tree, which root should be supported by Forking court.
234241
if (_parent == NULL_DISPUTE_KIT) {
235-
courts[FORKING_COURT].supportedDisputeKits[disputeKitNodes.length] = true;
242+
courts[FORKING_COURT].supportedDisputeKits[disputeKitID] = true;
243+
} else {
244+
depthLevel = disputeKitNodes[_parent].depthLevel + 1;
245+
// It should be always possible to reach the root from the leaf with the defined number of search iterations.
246+
require(depthLevel < SEARCH_ITERATIONS, "Depth level is at max");
236247
}
237-
disputeKitNodes.push(DisputeKitNode({parent: _parent, disputeKit: _disputeKitAddress}));
238-
}
239-
240-
/** @dev Changes the parent of an existing dispute kit.
241-
* @param _disputeKitID The ID of dispute kit.
242-
* @param _newParent The ID of the new parent dispute kit. It is left empty when root DK is created.
243-
* Note that the root DK must be supported by the forking court.
244-
*/
245-
function changeDisputeKitParent(uint256 _disputeKitID, uint256 _newParent) external onlyByGovernor {
246-
require(_disputeKitID < disputeKitNodes.length, "DisputeKitID doesn't exist");
247-
require(_newParent < disputeKitNodes.length, "NewParent doesn't exist");
248-
require(_newParent != _disputeKitID, "Invalid Parent");
249248

250-
// Create new tree, which root should be supported by Forking court.
251-
if (_newParent == NULL_DISPUTE_KIT) {
252-
courts[FORKING_COURT].supportedDisputeKits[_disputeKitID] = true;
253-
}
254-
disputeKitNodes[_disputeKitID].parent = _newParent;
249+
disputeKitNodes.push(
250+
DisputeKitNode({
251+
parent: _parent,
252+
children: new uint256[](0),
253+
disputeKit: _disputeKitAddress,
254+
depthLevel: depthLevel
255+
})
256+
);
257+
disputeKitNodes[_parent].children.push(disputeKitID);
255258
}
256259

257260
/** @dev Creates a subcourt under a specified parent court.
@@ -286,7 +289,10 @@ contract KlerosCore is IArbitrator {
286289
Court storage court = courts.push();
287290

288291
for (uint256 i = 0; i < _supportedDisputeKits.length; i++) {
289-
require(_supportedDisputeKits[i] < disputeKitNodes.length, "DK doesn't exist");
292+
require(
293+
_supportedDisputeKits[i] > 0 && _supportedDisputeKits[i] < disputeKitNodes.length,
294+
"Wrong DK index"
295+
);
290296
court.supportedDisputeKits[_supportedDisputeKits[i]] = true;
291297
}
292298

@@ -344,6 +350,14 @@ contract KlerosCore is IArbitrator {
344350
courts[_subcourtID].jurorsForCourtJump = _jurorsForCourtJump;
345351
}
346352

353+
/** @dev Changes the `hiddenVotes` property value of a specified subcourt.
354+
* @param _subcourtID The ID of the subcourt.
355+
* @param _hiddenVotes The new value for the `hiddenVotes` property value.
356+
*/
357+
function changeHiddenVotes(uint96 _subcourtID, bool _hiddenVotes) external onlyByGovernor {
358+
courts[_subcourtID].hiddenVotes = _hiddenVotes;
359+
}
360+
347361
/** @dev Changes the `timesPerPeriod` property value of a specified subcourt.
348362
* @param _subcourtID The ID of the subcourt.
349363
* @param _timesPerPeriod The new value for the `timesPerPeriod` property value.
@@ -368,6 +382,7 @@ contract KlerosCore is IArbitrator {
368382
Court storage subcourt = courts[_subcourtID];
369383
for (uint256 i = 0; i < _disputeKitIDs.length; i++) {
370384
if (_enable) {
385+
require(_disputeKitIDs[i] > 0 && _disputeKitIDs[i] < disputeKitNodes.length, "Wrong DK index");
371386
subcourt.supportedDisputeKits[_disputeKitIDs[i]] = true;
372387
} else {
373388
require(
@@ -527,34 +542,37 @@ contract KlerosCore is IArbitrator {
527542
"Access not allowed: Dispute Kit only."
528543
);
529544

530-
Court storage court = courts[dispute.subcourtID];
545+
uint96 newSubcourtID = dispute.subcourtID;
531546
uint256 newDisputeKitID = round.disputeKitID;
532547

533-
// Create a new round beforehand because dispute kit relies on the latest index.
548+
// Warning: the extra round must be created before calling disputeKit.createDispute()
534549
Round storage extraRound = dispute.rounds.push();
535550

536-
if (round.nbVotes >= court.jurorsForCourtJump) {
537-
{
538-
// Jump to parent subcourt.
539-
emit CourtJump(_disputeID, dispute.rounds.length - 1, dispute.subcourtID, court.parent);
540-
dispute.subcourtID = court.parent;
541-
court = courts[court.parent];
542-
}
543-
// TODO: Handle court jump in the Forking court.
544-
while (!court.supportedDisputeKits[newDisputeKitID]) {
545-
if (disputeKitNodes[newDisputeKitID].parent != 0) {
551+
if (round.nbVotes >= courts[newDisputeKitID].jurorsForCourtJump) {
552+
// Jump to parent subcourt.
553+
newSubcourtID = courts[newSubcourtID].parent;
554+
555+
for (uint256 i = 0; i < SEARCH_ITERATIONS; i++) {
556+
if (courts[newSubcourtID].supportedDisputeKits[newDisputeKitID]) {
557+
break;
558+
} else if (disputeKitNodes[newDisputeKitID].parent != NULL_DISPUTE_KIT) {
546559
newDisputeKitID = disputeKitNodes[newDisputeKitID].parent;
547560
} else {
548-
// If the root still isn't supported by the parent court fallback on the Forking court instead.
549-
// Note that root DK must be supported by Forking court by default but add this require as an extra check.
550-
require(
551-
courts[FORKING_COURT].supportedDisputeKits[newDisputeKitID] = true,
552-
"DK isn't supported by Forking court"
553-
);
554-
dispute.subcourtID = uint96(FORKING_COURT);
555-
break;
561+
// DK's parent has 0 index, that means we reached the root DK (0 depth level).
562+
// Jump to the next parent court if the current court doesn't support any DK from this tree.
563+
// Note that we don't reset newDisputeKitID in this case as, a precaution.
564+
newSubcourtID = courts[newSubcourtID].parent;
556565
}
557566
}
567+
// We didn't find a court that is compatible with DK from this tree, so we jump directly to the top court.
568+
// Note that this can only happen when disputeKitID is at its root, and each root DK is supported by the top court by default.
569+
if (!courts[newSubcourtID].supportedDisputeKits[newDisputeKitID]) {
570+
newSubcourtID = uint96(FORKING_COURT);
571+
}
572+
573+
if (newSubcourtID != dispute.subcourtID) {
574+
emit CourtJump(_disputeID, dispute.rounds.length - 1, dispute.subcourtID, newSubcourtID);
575+
}
558576

559577
// Dispute kit was changed, so create a dispute in the new DK contract.
560578
if (newDisputeKitID != round.disputeKitID) {
@@ -563,8 +581,11 @@ contract KlerosCore is IArbitrator {
563581
}
564582
}
565583

584+
dispute.subcourtID = newSubcourtID;
566585
dispute.period = Period.evidence;
567586
dispute.lastPeriodChange = block.timestamp;
587+
588+
Court storage court = courts[newSubcourtID];
568589
extraRound.nbVotes = msg.value / court.feeForJuror; // As many votes that can be afforded by the provided funds.
569590
extraRound.disputeKitID = newDisputeKitID;
570591
extraRound.tokensAtStakePerJuror = (court.minStake * court.alpha) / ALPHA_DIVISOR;
@@ -801,6 +822,14 @@ contract KlerosCore is IArbitrator {
801822
return courts[_subcourtID].supportedDisputeKits[_disputeKitID];
802823
}
803824

825+
/** @dev Gets non-primitive properties of a specified dispute kit node.
826+
* @param _disputeKitID The ID of the dispute kit.
827+
* @return children Indexes of children of this DK.
828+
*/
829+
function getDisputeKitChildren(uint256 _disputeKitID) external view returns (uint256[] memory) {
830+
return disputeKitNodes[_disputeKitID].children;
831+
}
832+
804833
/** @dev Gets the timesPerPeriod array for a given court.
805834
* @param _subcourtID The ID of the court to get the times from.
806835
* @return timesPerPeriod The timesPerPeriod array for the given court.

0 commit comments

Comments
 (0)