Skip to content

Commit

Permalink
Merge pull request #65 from kleros/feat/arbitration-phases
Browse files Browse the repository at this point in the history
feat: phases implementation
  • Loading branch information
jaybuidl committed May 27, 2022
2 parents 15ae9db + b42d718 commit 263bd18
Show file tree
Hide file tree
Showing 8 changed files with 515 additions and 79 deletions.
12 changes: 11 additions & 1 deletion contracts/deploy/00-home-chain-arbitration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,17 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
libraries: {
SortitionSumTreeFactory: sortitionSumTreeLibrary.address,
},
args: [deployer, pnk, AddressZero, disputeKit.address, false, minStake, alpha, feeForJuror, 3, [0, 0, 0, 0], 3],
args: [
deployer,
pnk,
AddressZero,
disputeKit.address,
[120, 120], // minStakingTime, maxFreezingTime
false,
[minStake, alpha, feeForJuror, 3], // minStake, alpha, feeForJuror, jurorsForCourtJump
[0, 0, 0, 0], // evidencePeriod, commitPeriod, votePeriod, appealPeriod
3,
],
log: true,
});

Expand Down
17 changes: 16 additions & 1 deletion contracts/src/arbitration/IDisputeKit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,14 @@ interface IDisputeKit {
function createDispute(
uint256 _coreDisputeID,
uint256 _numberOfChoices,
bytes calldata _extraData
bytes calldata _extraData,
uint256 _nbVotes
) external;

/** @dev Passes the phase.
*/
function passPhase() external;

/** @dev Draws the juror from the sortition tree. The drawn address is picked up by Kleros Core.
* Note: Access restricted to Kleros Core only.
* @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
Expand Down Expand Up @@ -130,4 +135,14 @@ interface IDisputeKit {
uint256 choice,
bool voted
);

/** @dev Returns the number of disputes without jurors in the dispute kit.
* @return The number of disputes without jurors in the dispute kit.
*/
function disputesWithoutJurors() external view returns (uint256);

/** @dev Returns true if the dispute kit is ready to Resolve, regardless of the number of disputes without jurors.
* @return Whether the dispute kit is ready to resolve, regardless of the number of disputes without jurors.
*/
function isResolving() external view returns (bool);
}
253 changes: 206 additions & 47 deletions contracts/src/arbitration/KlerosCore.sol

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions contracts/src/arbitration/dispute-kits/BaseDisputeKit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,10 @@ abstract contract BaseDisputeKit is IDisputeKit {
(bool success, ) = _destination.call{value: _amount}(_data);
require(success, "Unsuccessful call");
}

/** @dev Checks that the chosen address satisfies certain conditions for being drawn.
* @param _disputeID ID of the dispute in the core contract.
* @param _juror Chosen address.
*/
function postDrawCheck(uint256 _disputeID, address _juror) internal virtual returns (bool);
}
78 changes: 72 additions & 6 deletions contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,18 @@ import "../../evidence/IEvidence.sol";
* - a vote aggreation system: plurality,
* - an incentive system: equal split between coherent votes,
* - an appeal system: fund 2 choices only, vote on any choice.
* TODO:
* - phase management: Generating->Drawing->Resolving->Generating in coordination with KlerosCore to freeze staking.
*/
contract DisputeKitClassic is BaseDisputeKit, IEvidence {
// ************************************* //
// * Structs * //
// ************************************* //

enum Phase {
resolving, // No disputes that need drawing.
generating, // Waiting for a random number. Pass as soon as it is ready.
drawing // Jurors can be drawn.
}

struct Dispute {
Round[] rounds; // Rounds of the dispute. 0 is the default round, and [1, ..n] are the appeal rounds.
uint256 numberOfChoices; // The number of choices jurors have when voting. This does not include choice `0` which is reserved for "refuse to arbitrate".
Expand All @@ -49,6 +53,7 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
mapping(address => mapping(uint256 => uint256)) contributions; // Maps contributors to their contributions for each choice.
uint256 feeRewards; // Sum of reimbursable appeal fees available to the parties that made contributions to the ruling that ultimately wins a dispute.
uint256[] fundedChoices; // Stores the choices that are fully funded.
uint256 nbVotes; // Maximal number of votes this dispute can get.
}

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

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

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

event ChoiceFunded(uint256 indexed _disputeID, uint256 indexed _round, uint256 indexed _choice);
event NewPhaseDisputeKit(Phase _phase);

// ************************************* //
// * Modifiers * //
Expand Down Expand Up @@ -129,7 +139,7 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
/** @dev Changes the `core` storage variable.
* @param _core The new value for the `core` storage variable.
*/
function changeCore(address payable _core) external onlyByGovernor {
function changeCore(address _core) external onlyByGovernor {
core = KlerosCore(_core);
}

Expand All @@ -150,23 +160,55 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
* @param _coreDisputeID The ID of the dispute in Kleros Core.
* @param _numberOfChoices Number of choices of the dispute
* @param _extraData Additional info about the dispute, for possible use in future dispute kits.
* @param _nbVotes Number of votes for this dispute.
*/
function createDispute(
uint256 _coreDisputeID,
uint256 _numberOfChoices,
bytes calldata _extraData
bytes calldata _extraData,
uint256 _nbVotes
) external override onlyByCore {
uint256 localDisputeID = disputes.length;
Dispute storage dispute = disputes.push();
dispute.numberOfChoices = _numberOfChoices;
dispute.extraData = _extraData;

// New round in the Core should be created before the dispute creation in DK.
dispute.coreRoundIDToLocal[core.getNumberOfRounds(_coreDisputeID) - 1] = dispute.rounds.length;

Round storage round = dispute.rounds.push();
round.nbVotes = _nbVotes;
round.tied = true;

coreDisputeIDToLocal[_coreDisputeID] = localDisputeID;
disputesWithoutJurors++;
}

/** @dev Passes the phase.
*/
function passPhase() external override {
if (core.phase() == KlerosCore.Phase.staking || core.freezingPhaseTimeout()) {
require(phase != Phase.resolving, "Already in Resolving phase");
phase = Phase.resolving; // Safety net.
} else if (core.phase() == KlerosCore.Phase.freezing) {
if (phase == Phase.resolving) {
require(disputesWithoutJurors > 0, "All the disputes have jurors");
require(block.number >= core.getFreezeBlock() + 20, "Too soon: L1 finality required");
// TODO: RNG process is currently unfinished.
RNBlock = block.number;
rng.requestRN(block.number);
phase = Phase.generating;
} else if (phase == Phase.generating) {
RN = rng.getRN(RNBlock);
require(RN != 0, "Random number is not ready yet");
phase = Phase.drawing;
} else if (phase == Phase.drawing) {
require(disputesWithoutJurors == 0, "Not ready for Resolving phase");
phase = Phase.resolving;
}
}
// Should not be reached if the phase is unchanged.
emit NewPhaseDisputeKit(phase);
}

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

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

round.votes.push(Vote({account: drawnAddress, commit: bytes32(0), choice: 0, voted: false}));
if (postDrawCheck(_coreDisputeID, drawnAddress)) {
round.votes.push(Vote({account: drawnAddress, commit: bytes32(0), choice: 0, voted: false}));
if (round.votes.length == round.nbVotes) {
disputesWithoutJurors--;
}
} else {
drawnAddress = address(0);
}
}

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

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

Round storage newRound = dispute.rounds.push();
newRound.nbVotes = core.getNumberOfVotes(_coreDisputeID);
newRound.tied = true;
disputesWithoutJurors++;
}
core.appeal{value: appealCost}(_coreDisputeID, dispute.numberOfChoices, dispute.extraData);
}
Expand Down Expand Up @@ -572,10 +624,24 @@ contract DisputeKitClassic is BaseDisputeKit, IEvidence {
return (vote.account, vote.commit, vote.choice, vote.voted);
}

function isResolving() external view override returns (bool) {
return phase == Phase.resolving;
}

// ************************************* //
// * Internal * //
// ************************************* //

function postDrawCheck(uint256 _coreDisputeID, address _juror) internal view override returns (bool) {
uint256 subcourtID = core.getSubcourtID(_coreDisputeID);
(uint256 lockedAmountPerJuror, , , , , ) = core.getRoundInfo(
_coreDisputeID,
core.getNumberOfRounds(_coreDisputeID) - 1
);
(uint256 stakedTokens, uint256 lockedTokens) = core.getJurorBalance(_juror, uint96(subcourtID));
return stakedTokens >= lockedTokens + lockedAmountPerJuror;
}

/** @dev RNG function
* @return rn A random number.
*/
Expand Down
Loading

0 comments on commit 263bd18

Please sign in to comment.