@@ -77,6 +77,16 @@ contract KlerosCore is IArbitrator {
7777 bool disabled; // True if the dispute kit is disabled and can't be used. This parameter is added preemptively to avoid storage changes in the future.
7878 }
7979
80+ // Workaround "stack too deep" errors
81+ struct ExecuteParams {
82+ uint256 disputeID; // The ID of the dispute to execute.
83+ uint256 round; // The round to execute.
84+ uint256 coherentCount; // The number of coherent votes in the round.
85+ uint256 numberOfVotesInRound; // The number of votes in the round.
86+ uint256 penaltiesInRound; // The amount of tokens collected from penalties in the round.
87+ uint256 repartition; // The index of the repartition to execute.
88+ }
89+
8090 // ************************************* //
8191 // * Storage * //
8292 // ************************************* //
@@ -652,7 +662,10 @@ contract KlerosCore is IArbitrator {
652662 Round storage round = dispute.rounds[_round];
653663 IDisputeKit disputeKit = disputeKitNodes[round.disputeKitID].disputeKit;
654664
665+ uint256 start = round.repartitions;
655666 uint256 end = round.repartitions + _iterations;
667+ round.repartitions = end;
668+
656669 uint256 penaltiesInRoundCache = round.penalties; // For saving gas.
657670 uint256 numberOfVotesInRound = round.drawnJurors.length ;
658671 uint256 coherentCount = disputeKit.getCoherentCount (_disputeID, _round); // Total number of jurors that are eligible to a reward in this round.
@@ -664,51 +677,47 @@ contract KlerosCore is IArbitrator {
664677 // We loop over the votes twice, first to collect penalties, and second to distribute them as rewards along with arbitration fees.
665678 if (end > numberOfVotesInRound * 2 ) end = numberOfVotesInRound * 2 ;
666679 }
667- for (uint256 i = round.repartitions ; i < end; i++ ) {
680+ for (uint256 i = start ; i < end; i++ ) {
668681 if (i < numberOfVotesInRound) {
669682 penaltiesInRoundCache = _executePenalties (
670- _disputeID,
671- _round,
672- coherentCount,
673- numberOfVotesInRound,
674- penaltiesInRoundCache,
675- i
683+ ExecuteParams (_disputeID, _round, coherentCount, numberOfVotesInRound, penaltiesInRoundCache, i)
676684 );
677685 } else {
678- _executeRewards (_disputeID, _round, coherentCount, numberOfVotesInRound, penaltiesInRoundCache, i);
686+ _executeRewards (
687+ ExecuteParams (_disputeID, _round, coherentCount, numberOfVotesInRound, penaltiesInRoundCache, i)
688+ );
679689 }
680690 }
681691 if (round.penalties != penaltiesInRoundCache) {
682- round.penalties = penaltiesInRoundCache;
692+ round.penalties = penaltiesInRoundCache; // Reentrancy risk: breaks Check-Effect-Interact
683693 }
684- round.repartitions = end;
685694 }
686695
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];
696+ /// @dev Distribute the tokens and ETH for the specific round of the dispute, penalties only.
697+ /// @param _params The parameters for the execution, see `ExecuteParams`.
698+ /// @return penaltiesInRoundCache The updated penalties in round cache.
699+ function _executePenalties (ExecuteParams memory _params ) internal returns (uint256 ) {
700+ Dispute storage dispute = disputes[_params.disputeID];
701+ Round storage round = dispute.rounds[_params.round];
697702 IDisputeKit disputeKit = disputeKitNodes[round.disputeKitID].disputeKit;
698703
699704 // [0, 1] value that determines how coherent the juror was in this round, in basis points.
700- uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence (_disputeID, _round, _repartition);
705+ uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence (
706+ _params.disputeID,
707+ _params.round,
708+ _params.repartition
709+ );
701710 if (degreeOfCoherence > ALPHA_DIVISOR) {
702711 // Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
703712 degreeOfCoherence = ALPHA_DIVISOR;
704713 }
705714
706715 // Fully coherent jurors won't be penalized.
707716 uint256 penalty = (round.tokensAtStakePerJuror * (ALPHA_DIVISOR - degreeOfCoherence)) / ALPHA_DIVISOR;
708- _penaltiesInRound += penalty;
717+ _params.penaltiesInRound += penalty;
709718
710719 // Unlock the tokens affected by the penalty
711- address account = round.drawnJurors[_repartition ];
720+ address account = round.drawnJurors[_params.repartition ];
712721 jurors[account].lockedTokens[dispute.courtID] -= penalty;
713722
714723 // Apply the penalty to the staked tokens
@@ -720,79 +729,84 @@ contract KlerosCore is IArbitrator {
720729 // The juror does not have enough staked tokens after penalty for this court, unstake them.
721730 _setStakeForAccount (account, dispute.courtID, 0 , penalty);
722731 }
723- emit TokenAndETHShift (account, _disputeID, _round , degreeOfCoherence, - int256 (penalty), 0 );
732+ emit TokenAndETHShift (account, _params.disputeID, _params.round , degreeOfCoherence, - int256 (penalty), 0 );
724733
725- if (! disputeKit.isVoteActive (_disputeID, _round, _repartition )) {
734+ if (! disputeKit.isVoteActive (_params.disputeID, _params.round, _params.repartition )) {
726735 // The juror is inactive, unstake them.
727736 sortitionModule.setJurorInactive (account);
728737 }
729- if (_repartition == _numberOfVotesInRound - 1 && _coherentCount == 0 ) {
738+ if (_params.repartition == _params.numberOfVotesInRound - 1 && _params.coherentCount == 0 ) {
730739 // No one was coherent, send the rewards to the governor.
731740 payable (governor).send (round.totalFeesForJurors);
732- _safeTransfer (governor, _penaltiesInRound);
733- emit LeftoverRewardSent (_disputeID, _round, _penaltiesInRound, round.totalFeesForJurors);
741+ _safeTransfer (governor, _params.penaltiesInRound);
742+ emit LeftoverRewardSent (
743+ _params.disputeID,
744+ _params.round,
745+ _params.penaltiesInRound,
746+ round.totalFeesForJurors
747+ );
734748 }
735- return _penaltiesInRound ;
749+ return _params.penaltiesInRound ;
736750 }
737751
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];
752+ /// @dev Distribute the tokens and ETH for the specific round of the dispute, rewards only.
753+ /// @param _params The parameters for the execution, see `ExecuteParams`.
754+ function _executeRewards (ExecuteParams memory _params ) internal {
755+ Dispute storage dispute = disputes[_params.disputeID];
756+ Round storage round = dispute.rounds[_params.round];
748757 IDisputeKit disputeKit = disputeKitNodes[round.disputeKitID].disputeKit;
749758
750759 // [0, 1] value that determines how coherent the juror was in this round, in basis points.
751760 uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence (
752- _disputeID ,
753- _round ,
754- _repartition % _numberOfVotesInRound
761+ _params.disputeID ,
762+ _params.round ,
763+ _params.repartition % _params.numberOfVotesInRound
755764 );
756765
757766 // Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
758767 if (degreeOfCoherence > ALPHA_DIVISOR) {
759768 degreeOfCoherence = ALPHA_DIVISOR;
760769 }
761770
762- address account = round.drawnJurors[_repartition % _numberOfVotesInRound];
771+ address account = round.drawnJurors[_params.repartition % _params.numberOfVotesInRound];
772+ uint256 tokenLocked = (round.tokensAtStakePerJuror * degreeOfCoherence) / ALPHA_DIVISOR;
763773
764774 // 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;
775+ jurors[account].lockedTokens[dispute.courtID] -= tokenLocked;
768776
769777 // Give back the locked tokens in case the juror fully unstaked earlier.
770778 if (jurors[account].stakedTokens[dispute.courtID] == 0 ) {
771- uint256 tokenLocked = (round.tokensAtStakePerJuror * degreeOfCoherence) / ALPHA_DIVISOR;
772779 _safeTransfer (account, tokenLocked);
773780 }
774781
775782 // Transfer the rewards
776- uint256 tokenReward = ((_penaltiesInRound / _coherentCount ) * degreeOfCoherence) / ALPHA_DIVISOR;
783+ uint256 tokenReward = ((_params.penaltiesInRound / _params.coherentCount ) * degreeOfCoherence) / ALPHA_DIVISOR;
777784 round.sumTokenRewardPaid += tokenReward;
778- uint256 ethReward = ((round.totalFeesForJurors / _coherentCount ) * degreeOfCoherence) / ALPHA_DIVISOR;
785+ uint256 ethReward = ((round.totalFeesForJurors / _params.coherentCount ) * degreeOfCoherence) / ALPHA_DIVISOR;
779786 round.sumRewardPaid += ethReward;
780787 _safeTransfer (account, tokenReward);
781788 payable (account).send (ethReward);
782- emit TokenAndETHShift (account, _disputeID, _round, degreeOfCoherence, int256 (tokenReward), int256 (ethReward));
789+ emit TokenAndETHShift (
790+ account,
791+ _params.disputeID,
792+ _params.round,
793+ degreeOfCoherence,
794+ int256 (tokenReward),
795+ int256 (ethReward)
796+ );
783797
784798 // Transfer any residual rewards to the governor. It may happen due to partial coherence of the jurors.
785- if (_repartition == _numberOfVotesInRound * 2 - 1 ) {
799+ if (_params.repartition == _params.numberOfVotesInRound * 2 - 1 ) {
800+ uint256 leftoverTokenReward = _params.penaltiesInRound - round.sumTokenRewardPaid;
786801 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- }
802+ if (leftoverTokenReward != 0 || leftoverReward != 0 ) {
792803 if (leftoverTokenReward != 0 ) {
793804 _safeTransfer (governor, leftoverTokenReward);
794805 }
795- emit LeftoverRewardSent (_disputeID, _round, leftoverTokenReward, leftoverReward);
806+ if (leftoverReward != 0 ) {
807+ payable (governor).send (leftoverReward);
808+ }
809+ emit LeftoverRewardSent (_params.disputeID, _params.round, leftoverTokenReward, leftoverReward);
796810 }
797811 }
798812 }
0 commit comments