Skip to content

Commit b7418c0

Browse files
committed
refactor: extracted distinct rewards and penalties execution functions
1 parent 119785a commit b7418c0

File tree

1 file changed

+115
-96
lines changed

1 file changed

+115
-96
lines changed

contracts/src/arbitration/KlerosCore.sol

Lines changed: 115 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)