Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: add iterations for DK jump + hidden votes setter #71

Merged
merged 7 commits into from
May 19, 2022
111 changes: 70 additions & 41 deletions contracts/src/arbitration/KlerosCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ contract KlerosCore is IArbitrator {

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

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

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

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

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

// Create new tree, which root should be supported by Forking court.
if (_parent == NULL_DISPUTE_KIT) {
courts[FORKING_COURT].supportedDisputeKits[disputeKitNodes.length] = true;
courts[FORKING_COURT].supportedDisputeKits[disputeKitID] = true;
} else {
depthLevel = disputeKitNodes[_parent].depthLevel + 1;
// It should be always possible to reach the root from the leaf with the defined number of search iterations.
require(depthLevel < SEARCH_ITERATIONS, "Depth level is at max");
}
disputeKitNodes.push(DisputeKitNode({parent: _parent, disputeKit: _disputeKitAddress}));
}

/** @dev Changes the parent of an existing dispute kit.
* @param _disputeKitID The ID of dispute kit.
* @param _newParent The ID of the new parent dispute kit. It is left empty when root DK is created.
* Note that the root DK must be supported by the forking court.
*/
function changeDisputeKitParent(uint256 _disputeKitID, uint256 _newParent) external onlyByGovernor {
require(_disputeKitID < disputeKitNodes.length, "DisputeKitID doesn't exist");
require(_newParent < disputeKitNodes.length, "NewParent doesn't exist");
require(_newParent != _disputeKitID, "Invalid Parent");

// Create new tree, which root should be supported by Forking court.
if (_newParent == NULL_DISPUTE_KIT) {
courts[FORKING_COURT].supportedDisputeKits[_disputeKitID] = true;
}
disputeKitNodes[_disputeKitID].parent = _newParent;
disputeKitNodes.push(
DisputeKitNode({
parent: _parent,
children: new uint256[](0),
disputeKit: _disputeKitAddress,
depthLevel: depthLevel
})
);
disputeKitNodes[_parent].children.push(disputeKitID);
}

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

for (uint256 i = 0; i < _supportedDisputeKits.length; i++) {
require(_supportedDisputeKits[i] < disputeKitNodes.length, "DK doesn't exist");
require(
_supportedDisputeKits[i] > 0 && _supportedDisputeKits[i] < disputeKitNodes.length,
"Wrong DK index"
);
court.supportedDisputeKits[_supportedDisputeKits[i]] = true;
}

Expand Down Expand Up @@ -344,6 +350,14 @@ contract KlerosCore is IArbitrator {
courts[_subcourtID].jurorsForCourtJump = _jurorsForCourtJump;
}

/** @dev Changes the `hiddenVotes` property value of a specified subcourt.
* @param _subcourtID The ID of the subcourt.
* @param _hiddenVotes The new value for the `hiddenVotes` property value.
*/
function changeHiddenVotes(uint96 _subcourtID, bool _hiddenVotes) external onlyByGovernor {
courts[_subcourtID].hiddenVotes = _hiddenVotes;
}

/** @dev Changes the `timesPerPeriod` property value of a specified subcourt.
* @param _subcourtID The ID of the subcourt.
* @param _timesPerPeriod The new value for the `timesPerPeriod` property value.
Expand All @@ -368,6 +382,7 @@ contract KlerosCore is IArbitrator {
Court storage subcourt = courts[_subcourtID];
for (uint256 i = 0; i < _disputeKitIDs.length; i++) {
if (_enable) {
require(_disputeKitIDs[i] > 0 && _disputeKitIDs[i] < disputeKitNodes.length, "Wrong DK index");
subcourt.supportedDisputeKits[_disputeKitIDs[i]] = true;
} else {
require(
Expand Down Expand Up @@ -527,34 +542,37 @@ contract KlerosCore is IArbitrator {
"Access not allowed: Dispute Kit only."
);

Court storage court = courts[dispute.subcourtID];
uint96 newSubcourtID = dispute.subcourtID;
uint256 newDisputeKitID = round.disputeKitID;

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

if (round.nbVotes >= court.jurorsForCourtJump) {
{
// Jump to parent subcourt.
emit CourtJump(_disputeID, dispute.rounds.length - 1, dispute.subcourtID, court.parent);
dispute.subcourtID = court.parent;
court = courts[court.parent];
}
// TODO: Handle court jump in the Forking court.
while (!court.supportedDisputeKits[newDisputeKitID]) {
if (disputeKitNodes[newDisputeKitID].parent != 0) {
if (round.nbVotes >= courts[newDisputeKitID].jurorsForCourtJump) {
// Jump to parent subcourt.
newSubcourtID = courts[newSubcourtID].parent;

for (uint256 i = 0; i < SEARCH_ITERATIONS; i++) {
if (courts[newSubcourtID].supportedDisputeKits[newDisputeKitID]) {
break;
} else if (disputeKitNodes[newDisputeKitID].parent != NULL_DISPUTE_KIT) {
newDisputeKitID = disputeKitNodes[newDisputeKitID].parent;
} else {
// If the root still isn't supported by the parent court fallback on the Forking court instead.
// Note that root DK must be supported by Forking court by default but add this require as an extra check.
require(
courts[FORKING_COURT].supportedDisputeKits[newDisputeKitID] = true,
"DK isn't supported by Forking court"
);
dispute.subcourtID = uint96(FORKING_COURT);
break;
// DK's parent has 0 index, that means we reached the root DK (0 depth level).
// Jump to the next parent court if the current court doesn't support any DK from this tree.
// Note that we don't reset newDisputeKitID in this case as, a precaution.
newSubcourtID = courts[newSubcourtID].parent;
}
}
// We didn't find a court that is compatible with DK from this tree, so we jump directly to the top court.
// Note that this can only happen when disputeKitID is at its root, and each root DK is supported by the top court by default.
if (!courts[newSubcourtID].supportedDisputeKits[newDisputeKitID]) {
newSubcourtID = uint96(FORKING_COURT);
}

if (newSubcourtID != dispute.subcourtID) {
emit CourtJump(_disputeID, dispute.rounds.length - 1, dispute.subcourtID, newSubcourtID);
}

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

dispute.subcourtID = newSubcourtID;
dispute.period = Period.evidence;
dispute.lastPeriodChange = block.timestamp;

Court storage court = courts[newSubcourtID];
extraRound.nbVotes = msg.value / court.feeForJuror; // As many votes that can be afforded by the provided funds.
extraRound.disputeKitID = newDisputeKitID;
extraRound.tokensAtStakePerJuror = (court.minStake * court.alpha) / ALPHA_DIVISOR;
Expand Down Expand Up @@ -801,6 +822,14 @@ contract KlerosCore is IArbitrator {
return courts[_subcourtID].supportedDisputeKits[_disputeKitID];
}

/** @dev Gets non-primitive properties of a specified dispute kit node.
* @param _disputeKitID The ID of the dispute kit.
* @return children Indexes of children of this DK.
*/
function getDisputeKitChildren(uint256 _disputeKitID) external view returns (uint256[] memory) {
return disputeKitNodes[_disputeKitID].children;
}

/** @dev Gets the timesPerPeriod array for a given court.
* @param _subcourtID The ID of the court to get the times from.
* @return timesPerPeriod The timesPerPeriod array for the given court.
Expand Down