@@ -657,103 +657,144 @@ contract KlerosCore is IArbitrator {
657657 uint256 numberOfVotesInRound = round.drawnJurors.length ;
658658 uint256 coherentCount = disputeKit.getCoherentCount (_disputeID, _round); // Total number of jurors that are eligible to a reward in this round.
659659
660- address account; // Address of the juror.
661- uint256 degreeOfCoherence; // [0, 1] value that determines how coherent the juror was in this round, in basis points.
662-
663660 if (coherentCount == 0 ) {
664661 // We loop over the votes once as there are no rewards because it is not a tie and no one in this round is coherent with the final outcome.
665662 if (end > numberOfVotesInRound) end = numberOfVotesInRound;
666663 } else {
667664 // We loop over the votes twice, first to collect penalties, and second to distribute them as rewards along with arbitration fees.
668665 if (end > numberOfVotesInRound * 2 ) end = numberOfVotesInRound * 2 ;
669666 }
670-
671667 for (uint256 i = round.repartitions; i < end; i++ ) {
672668 if (i < numberOfVotesInRound) {
673- // Penalty.
674- degreeOfCoherence = disputeKit.getDegreeOfCoherence (_disputeID, _round, i);
669+ penaltiesInRoundCache = _executePenalties (
670+ _disputeID,
671+ _round,
672+ coherentCount,
673+ numberOfVotesInRound,
674+ penaltiesInRoundCache,
675+ i
676+ );
677+ } else {
678+ _executeRewards (_disputeID, _round, coherentCount, numberOfVotesInRound, penaltiesInRoundCache, i);
679+ }
680+ }
681+ if (round.penalties != penaltiesInRoundCache) {
682+ round.penalties = penaltiesInRoundCache;
683+ }
684+ round.repartitions = end;
685+ }
675686
676- // Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
677- if (degreeOfCoherence > ALPHA_DIVISOR) {
678- degreeOfCoherence = ALPHA_DIVISOR;
679- }
687+ function _executePenalties (
688+ uint256 _disputeID ,
689+ uint256 _round ,
690+ uint256 _coherentCount ,
691+ uint256 _numberOfVotesInRound ,
692+ uint256 _penaltiesInRound ,
693+ uint256 _repartition
694+ ) internal returns (uint256 ) {
695+ Dispute storage dispute = disputes[_disputeID];
696+ Round storage round = dispute.rounds[_round];
697+ IDisputeKit disputeKit = disputeKitNodes[round.disputeKitID].disputeKit;
680698
681- // Fully coherent jurors won't be penalized.
682- uint256 penalty = (round.tokensAtStakePerJuror * (ALPHA_DIVISOR - degreeOfCoherence)) / ALPHA_DIVISOR;
683- penaltiesInRoundCache += penalty;
699+ // [0, 1] value that determines how coherent the juror was in this round, in basis points.
700+ uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence (_disputeID, _round, _repartition);
701+ if (degreeOfCoherence > ALPHA_DIVISOR) {
702+ // Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
703+ degreeOfCoherence = ALPHA_DIVISOR;
704+ }
684705
685- account = round.drawnJurors[i];
686- jurors[account].lockedTokens[dispute.courtID] -= penalty; // Release this part of locked tokens.
706+ // Fully coherent jurors won't be penalized.
707+ uint256 penalty = (round.tokensAtStakePerJuror * (ALPHA_DIVISOR - degreeOfCoherence)) / ALPHA_DIVISOR;
708+ _penaltiesInRound += penalty;
709+
710+ // Unlock the tokens affected by the penalty
711+ address account = round.drawnJurors[_repartition];
712+ jurors[account].lockedTokens[dispute.courtID] -= penalty;
713+
714+ // Apply the penalty to the staked tokens
715+ if (jurors[account].stakedTokens[dispute.courtID] >= courts[dispute.courtID].minStake + penalty) {
716+ // The juror still has enough staked token after penalty for this court.
717+ uint256 newStake = jurors[account].stakedTokens[dispute.courtID] - penalty;
718+ _setStakeForAccount (account, dispute.courtID, newStake, penalty);
719+ } else if (jurors[account].stakedTokens[dispute.courtID] != 0 ) {
720+ // The juror does not have enough staked tokens after penalty for this court, unstake them.
721+ _setStakeForAccount (account, dispute.courtID, 0 , penalty);
722+ }
723+ emit TokenAndETHShift (account, _disputeID, _round, degreeOfCoherence, - int256 (penalty), 0 );
687724
688- // Can only update the stake if it is able to cover the minStake and penalty, otherwise unstake from the court.
689- if (jurors[account].stakedTokens[dispute.courtID] >= courts[dispute.courtID].minStake + penalty) {
690- uint256 newStake = jurors[account].stakedTokens[dispute.courtID] - penalty;
691- _setStakeForAccount (account, dispute.courtID, newStake, penalty);
692- } else if (jurors[account].stakedTokens[dispute.courtID] != 0 ) {
693- _setStakeForAccount (account, dispute.courtID, 0 , penalty);
694- }
725+ if (! disputeKit.isVoteActive (_disputeID, _round, _repartition)) {
726+ // The juror is inactive, unstake them.
727+ sortitionModule.setJurorInactive (account);
728+ }
729+ if (_repartition == _numberOfVotesInRound - 1 && _coherentCount == 0 ) {
730+ // No one was coherent, send the rewards to the governor.
731+ payable (governor).send (round.totalFeesForJurors);
732+ _safeTransfer (governor, _penaltiesInRound);
733+ emit LeftoverRewardSent (_disputeID, _round, _penaltiesInRound, round.totalFeesForJurors);
734+ }
735+ return _penaltiesInRound;
736+ }
695737
696- // Unstake the juror if he lost due to inactivity.
697- if (! disputeKit.isVoteActive (_disputeID, _round, i)) {
698- sortitionModule.setJurorInactive (account);
699- }
700- emit TokenAndETHShift (account, _disputeID, _round, degreeOfCoherence, - int256 (penalty), 0 );
701-
702- if (i == numberOfVotesInRound - 1 ) {
703- if (coherentCount == 0 ) {
704- // No one was coherent. Send the rewards to governor.
705- payable (governor).send (round.totalFeesForJurors);
706- _safeTransfer (governor, penaltiesInRoundCache);
707- emit LeftoverRewardSent (_disputeID, _round, penaltiesInRoundCache, round.totalFeesForJurors);
708- }
709- }
710- } else {
711- // Reward.
712- degreeOfCoherence = disputeKit.getDegreeOfCoherence (_disputeID, _round, i % numberOfVotesInRound);
738+ function _executeRewards (
739+ uint256 _disputeID ,
740+ uint256 _round ,
741+ uint256 _coherentCount ,
742+ uint256 _numberOfVotesInRound ,
743+ uint256 _penaltiesInRound ,
744+ uint256 _repartition
745+ ) internal {
746+ Dispute storage dispute = disputes[_disputeID];
747+ Round storage round = dispute.rounds[_round];
748+ IDisputeKit disputeKit = disputeKitNodes[round.disputeKitID].disputeKit;
713749
714- // Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
715- if (degreeOfCoherence > ALPHA_DIVISOR) {
716- degreeOfCoherence = ALPHA_DIVISOR;
717- }
750+ // [0, 1] value that determines how coherent the juror was in this round, in basis points.
751+ uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence (
752+ _disputeID,
753+ _round,
754+ _repartition % _numberOfVotesInRound
755+ );
718756
719- account = round.drawnJurors[i % numberOfVotesInRound];
757+ // Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
758+ if (degreeOfCoherence > ALPHA_DIVISOR) {
759+ degreeOfCoherence = ALPHA_DIVISOR;
760+ }
720761
721- // Release the rest of the tokens of the juror for this round.
722- jurors[account].lockedTokens[dispute.courtID] -=
723- (round.tokensAtStakePerJuror * degreeOfCoherence) /
724- ALPHA_DIVISOR;
762+ address account = round.drawnJurors[_repartition % _numberOfVotesInRound];
725763
726- // Give back the locked tokens in case the juror fully unstaked earlier.
727- if (jurors[account].stakedTokens[dispute.courtID] == 0 ) {
728- uint256 tokenLocked = (round.tokensAtStakePerJuror * degreeOfCoherence) / ALPHA_DIVISOR;
729- _safeTransfer (account, tokenLocked);
730- }
764+ // Release the rest of the tokens of the juror for this round.
765+ jurors[account].lockedTokens[dispute.courtID] -=
766+ (round.tokensAtStakePerJuror * degreeOfCoherence) /
767+ ALPHA_DIVISOR;
731768
732- uint256 tokenReward = ((penaltiesInRoundCache / coherentCount) * degreeOfCoherence) / ALPHA_DIVISOR;
733- round.sumTokenRewardPaid += tokenReward;
734- uint256 ethReward = ((round.totalFeesForJurors / coherentCount) * degreeOfCoherence) / ALPHA_DIVISOR;
735- round.sumRewardPaid += ethReward;
736- _safeTransfer (account, tokenReward);
737- payable (account).send (ethReward);
738- emit TokenAndETHShift (
739- account,
740- _disputeID,
741- _round,
742- degreeOfCoherence,
743- int256 (tokenReward),
744- int256 (ethReward)
745- );
769+ // Give back the locked tokens in case the juror fully unstaked earlier.
770+ if (jurors[account].stakedTokens[dispute.courtID] == 0 ) {
771+ uint256 tokenLocked = (round.tokensAtStakePerJuror * degreeOfCoherence) / ALPHA_DIVISOR;
772+ _safeTransfer (account, tokenLocked);
773+ }
746774
747- if (i == numberOfVotesInRound * 2 - 1 ) {
748- _executeResidualRewards (_disputeID, _round, penaltiesInRoundCache); // distinct function to avoid a "stack too deep error"
775+ // Transfer the rewards
776+ uint256 tokenReward = ((_penaltiesInRound / _coherentCount) * degreeOfCoherence) / ALPHA_DIVISOR;
777+ round.sumTokenRewardPaid += tokenReward;
778+ uint256 ethReward = ((round.totalFeesForJurors / _coherentCount) * degreeOfCoherence) / ALPHA_DIVISOR;
779+ round.sumRewardPaid += ethReward;
780+ _safeTransfer (account, tokenReward);
781+ payable (account).send (ethReward);
782+ emit TokenAndETHShift (account, _disputeID, _round, degreeOfCoherence, int256 (tokenReward), int256 (ethReward));
783+
784+ // Transfer any residual rewards to the governor. It may happen due to partial coherence of the jurors.
785+ if (_repartition == _numberOfVotesInRound * 2 - 1 ) {
786+ uint256 leftoverReward = round.totalFeesForJurors - round.sumRewardPaid;
787+ uint256 leftoverTokenReward = _penaltiesInRound - round.sumTokenRewardPaid;
788+ if (leftoverReward != 0 || leftoverTokenReward != 0 ) {
789+ if (leftoverReward != 0 ) {
790+ payable (governor).send (leftoverReward);
791+ }
792+ if (leftoverTokenReward != 0 ) {
793+ _safeTransfer (governor, leftoverTokenReward);
749794 }
795+ emit LeftoverRewardSent (_disputeID, _round, leftoverTokenReward, leftoverReward);
750796 }
751797 }
752-
753- if (round.penalties != penaltiesInRoundCache) {
754- round.penalties = penaltiesInRoundCache;
755- }
756- round.repartitions = end;
757798 }
758799
759800 /// @dev Executes a specified dispute's ruling. UNTRUSTED.
@@ -949,28 +990,6 @@ contract KlerosCore is IArbitrator {
949990 emit DisputeKitEnabled (_courtID, _disputeKitID, _enable);
950991 }
951992
952- /// @dev Sends the residual rewards to the governor. It may happen due to partial coherence of the jurors.
953- /// @param _disputeID The ID of the dispute.
954- /// @param _round The round to execute.
955- /// @param _penaltiesInRound The total penalties in the round.
956- function _executeResidualRewards (uint256 _disputeID , uint256 _round , uint256 _penaltiesInRound ) internal {
957- Dispute storage dispute = disputes[_disputeID];
958- Round storage round = dispute.rounds[_round];
959- uint256 leftoverReward;
960- uint256 leftoverTokenReward;
961- if (round.totalFeesForJurors > round.sumRewardPaid) {
962- leftoverReward = round.totalFeesForJurors - round.sumRewardPaid;
963- payable (governor).send (leftoverReward);
964- }
965- if (_penaltiesInRound > round.sumTokenRewardPaid) {
966- leftoverTokenReward = _penaltiesInRound - round.sumTokenRewardPaid;
967- _safeTransfer (governor, leftoverTokenReward);
968- }
969- if (leftoverReward != 0 || leftoverTokenReward != 0 ) {
970- emit LeftoverRewardSent (_disputeID, _round, leftoverTokenReward, leftoverReward);
971- }
972- }
973-
974993 /// @dev Sets the specified juror's stake in a court.
975994 /// `O(n + p * log_k(j))` where
976995 /// `n` is the number of courts the juror has staked in,
0 commit comments